noema 0.4.0

Noema IOC and DI framework for Rust
Documentation
/// Registers a concrete type with an explicit lifecycle.
///
/// # Syntax
///
/// ```ignore
/// dependency!(singleton, MyService);
/// dependency!(transient, RequestId);
/// ```
///
/// Resolution: `resolve::<MyService>()` → `Arc<MyService>`.
///
/// Invoking the macro twice for the same type is a **compile error**.
#[macro_export]
macro_rules! dependency {
    ($lifecycle:ident, $type:ty) => {
        $crate::__dependency!($lifecycle, concrete, $type);
    };
}

/// Registers a trait binding with an explicit lifecycle.
///
/// # Syntax
///
/// ```ignore
/// trait Logger: Send + Sync { /* ... */ }
/// dependency_as!(singleton, Logger: ConsoleLogger);
/// dependency_as!(transient, Logger: FileLogger);
/// ```
///
/// Resolution: `resolve::<dyn Logger + Send + Sync>()`.
#[macro_export]
macro_rules! dependency_as {
    ($lifecycle:ident, $trait:path: $impl:ty) => {
        $crate::__dependency!($lifecycle, r#as, dyn $trait + Send + Sync, $impl);
    };
}

/// Registers a **many** batch: each entry declares its own lifecycle.
///
/// # Syntax
///
/// ```ignore
/// dependency_as_many!(Validator: [
///     (singleton, EmailValidator),
///     (transient, SmsValidator),
/// ]);
/// ```
///
/// Resolution: `resolve::<Arc<[Arc<dyn Validator + Send + Sync>]>>()`.
#[macro_export]
macro_rules! dependency_as_many {
    ($trait:path: [ $( ($lifecycle:ident, $impl:ty) ),* $(,)? ]) => {
        $crate::__many_batch_mixed!(@emit dyn $trait + Send + Sync, [ $( ($lifecycle, $impl) ),* ]);
    };
}

/// Registers a **keyed** batch: each entry declares key and lifecycle.
///
/// # Syntax
///
/// ```ignore
/// #[derive(PartialEq)]
/// enum PaymentKey { Stripe, Paypal }
///
/// dependency_keyed_as!(PaymentKey, Payment: [
///     (PaymentKey::Stripe, singleton, StripeGateway),
///     (PaymentKey::Paypal, transient, PaypalGateway),
/// ]);
/// ```
///
/// Resolution: `resolve::<Keyed<PaymentKey, dyn Payment + Send + Sync>>().one(PaymentKey::Stripe)`.
#[macro_export]
macro_rules! dependency_keyed_as {
    ($key:ty, $trait:path: [ $( ($key_expr:expr, $lifecycle:ident, $impl:ty) ),* $(,)? ]) => {
        $crate::__keyed_batch_mixed!(@emit $key, dyn $trait + Send + Sync, [ $( ($key_expr, $lifecycle, $impl) ),* ]);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __resolve_entry {
    (singleton, $trait:ty, $impl:ty) => {{
        use std::sync::{Arc, LazyLock};
        use $crate::core::{Container, Injectable};

        static CACHED: LazyLock<Arc<$trait>> = LazyLock::new(|| {
            Arc::new(<$impl as Injectable<Container>>::inject(&Container)) as Arc<$trait>
        });
        CACHED.clone()
    }};
    (transient, $trait:ty, $impl:ty) => {{
        use std::sync::Arc;
        use $crate::core::{Container, Injectable};

        Arc::new(<$impl as Injectable<Container>>::inject(&Container)) as Arc<$trait>
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __many_batch_mixed {
    (@emit $trait:ty, [ $( ($lifecycle:ident, $impl:ty) ),* $(,)? ]) => {
        const _: () = {
            use $crate::core::Container;
            use $crate::di::ManyResolver;
            use std::sync::Arc;

            impl ManyResolver<$trait> for Container {
                fn resolve_many() -> Arc<[Arc<$trait>]> {
                    Arc::from([
                        $( $crate::__resolve_entry!($lifecycle, $trait, $impl), )*
                    ])
                }
            }
        };
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __keyed_batch_mixed {
    (@emit $key:ty, $trait:ty, [ $( ($key_expr:expr, $lifecycle:ident, $impl:ty) ),* $(,)? ]) => {
        const _: () = {
            use $crate::core::Container;
            use $crate::di::KeyedResolver;
            use std::sync::Arc;

            impl KeyedResolver<$key, $trait> for Container {
                fn resolve_keyed(key: $key) -> Option<Arc<$trait>> {
                    $(
                        if key == $key_expr {
                            return Some($crate::__resolve_entry!($lifecycle, $trait, $impl));
                        }
                    )*
                    None
                }
            }
        };
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __dependency {
    // singleton | transient — concrete type
    ($lifecycle:ident, concrete, $type:ty) => {
        const _: () = {
            use std::sync::Arc;
            use $crate::core::{Container, Injectable, Resolver};

            $crate::__dependency!(@lifecycle $lifecycle, {
                use std::sync::LazyLock;
                impl Resolver<$type> for Container {
                    fn resolve() -> Arc<$type> {
                        static INSTANCE: LazyLock<Arc<$type>> = LazyLock::new(|| {
                            Arc::new(<$type as Injectable<Container>>::inject(&Container))
                        });
                        INSTANCE.clone()
                    }
                }
            }, {
                impl Resolver<$type> for Container {
                    fn resolve() -> Arc<$type> {
                        Arc::new(<$type as Injectable<Container>>::inject(&Container))
                    }
                }
            });
        };
    };

    // singleton | transient — trait binding (standalone inject)
    ($lifecycle:ident, r#as, $trait:ty, $impl:ty) => {
        const _: () = {
            use std::sync::Arc;
            use $crate::core::{Container, Injectable, Resolver};

            $crate::__dependency!(@lifecycle $lifecycle, {
                use std::sync::LazyLock;
                impl Resolver<$trait> for Container {
                    fn resolve() -> Arc<$trait> {
                        static INSTANCE: LazyLock<Arc<$trait>> = LazyLock::new(|| {
                            Arc::new(<$impl as Injectable<Container>>::inject(&Container))
                                as Arc<$trait>
                        });
                        INSTANCE.clone()
                    }
                }
            }, {
                impl Resolver<$trait> for Container {
                    fn resolve() -> Arc<$trait> {
                        Arc::new(<$impl as Injectable<Container>>::inject(&Container))
                            as Arc<$trait>
                    }
                }
            });
        };
    };

    (@lifecycle singleton, $singleton:expr, $transient:expr) => {
        $singleton
    };
    (@lifecycle transient, $singleton:expr, $transient:expr) => {
        $transient
    };
}