nject-macro 0.5.0

Zero cost dependency injection macros
Documentation
#![allow(clippy::needless_doctest_main)]
#![doc = include_str!("../README.md")]
mod core;
mod finalize;
mod init;
mod inject;
mod injectable;
mod module;
mod provider;
use finalize::handle_finalize_imports;
use init::handle_init;
use inject::handle_inject;
use injectable::handle_injectable;
use module::handle_module;
use proc_macro::TokenStream;
use provider::handle_provider;

/// For internal purposes only. Should not be used.
#[proc_macro_derive(InjectableHelperAttr, attributes(inject))]
pub fn injectable_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

/// For internal purposes only. Should not be used.
#[proc_macro_derive(ModuleHelperAttr, attributes(export))]
pub fn module_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

/// For internal purposes only. Should not be used.
#[proc_macro_derive(ProviderHelperAttr, attributes(import, provide, scope))]
pub fn provider_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

/// For internal purposes only. Should not be used.
#[proc_macro_derive(ScopeHelperAttr, attributes(arg))]
pub fn scope_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

/// Mark a struct as injectable.
/// ```rust
/// use nject::{injectable, provider};
///
/// #[injectable]
/// struct Facade;
///
/// #[provider]
/// struct Provider;
///
/// let facade: Facade = Provider.provide();
/// ```
#[proc_macro_attribute]
pub fn injectable(_attr: TokenStream, item: TokenStream) -> TokenStream {
    handle_injectable(item).unwrap_or_else(|e| e.to_compile_error().into())
}

/// Use the given value to inject.
/// ```rust
/// use nject::{inject, injectable, provider};
///
/// #[inject(Self { value: 42 })]
/// struct DepOne {
///     value: i32,
/// }
///
/// #[inject(|injectable_dep: DepOne| Self(12, injectable_dep))]
/// struct DepTwo(i32, DepOne);
///
/// #[injectable]
/// struct Facade(DepOne, DepTwo, #[inject(123)] i32);
///
/// #[provider]
/// struct Provider;
///
/// let facade: Facade = Provider.provide();
/// ```
#[proc_macro_attribute]
pub fn inject(attr: TokenStream, item: TokenStream) -> TokenStream {
    handle_inject(item, attr).unwrap_or_else(|e| e.to_compile_error().into())
}

/// Provide a value for a specific type.
/// ```rust
/// use nject::{injectable, provider};
///
/// struct Dependency {
///     value: i32,
/// }
///
/// struct SharedDependency {
///     value: i32,
/// }
///
/// #[injectable]
/// struct Facade<'a>(Dependency, &'a SharedDependency);
///
/// #[provider]
/// #[provide(Dependency, Dependency { value: 123 })]
/// struct Provider {
///     #[provide]
///     shared: SharedDependency
/// }
///
/// let provider = Provider { shared: SharedDependency { value: 456 } };
/// let dependency: Dependency = provider.provide();
/// let facade: Facade = provider.provide();
/// ```
#[proc_macro_attribute]
pub fn provider(_attr: TokenStream, item: TokenStream) -> TokenStream {
    handle_provider(item).unwrap_or_else(|e| e.to_compile_error().into())
}

/// Declare a module to export internal types.
/// ```rust
/// use nject::{injectable, provider};
///
/// mod sub {
///     use nject::{injectable, module};
///     use std::rc::Rc;
///
///     #[injectable]
///     struct InternalType(#[inject(123)] i32); // Not visible outside of module.
///
///     #[injectable]
///     pub struct Facade<'a> {
///         hidden: &'a InternalType,
///         public: Rc<i32>,
///     }
///
///     #[injectable]
///     #[module]
///     // Public type exports must be made on the struct (not the fields).
///     // To prevent name collisions, use absolute paths in types.
///     #[export(std::rc::Rc<i32>, self.public.clone())]
///     pub struct Module {
///         #[export] // Fields exports are for internal types.
///         hidden: InternalType,
///         #[inject(Rc::new(456))]
///         public: Rc<i32>,
///     }
/// }
///
/// #[injectable]
/// #[provider]
/// struct Provider {
///     #[import]
///     // To import module public exports, use the absolute path to its definition.
///     sub_mod: crate::sub::Module,
/// }
///
/// #[provider]
/// struct InitProvider;
///
/// fn main() {
///     let provider = InitProvider.provide::<Provider>();
///     let facade = provider.provide::<sub::Facade>();
/// }
/// ```
#[proc_macro_attribute]
pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream {
    handle_module(attr, item).unwrap_or_else(|e| e.to_compile_error().into())
}

/// Simplify provider initialisation by automatically creating the chain of intermediate providers.
///
/// Instead of manually defining intermediate providers and chaining `.provide()` calls,
/// use `init!` with the list of module types in dependency order.
///
/// # Expression form
///
/// Returns the provider as an owned value. Works with struct-level `#[export]` (owned values).
///
/// ```rust
/// use nject::{init, injectable, module, provider};
///
/// #[injectable]
/// #[module]
/// #[export(i32, 42)]
/// struct ConfigModule;
///
/// #[injectable]
/// #[module]
/// #[export(String, |x: i32| format!("value: {}", x))]
/// struct FormatModule;
///
/// #[injectable]
/// #[provider]
/// struct AppProvider(#[import] ConfigModule, #[import] FormatModule);
///
/// let provider: AppProvider = init!(ConfigModule, FormatModule);
/// let greeting: String = provider.provide();
/// assert_eq!(greeting, "value: 42");
/// ```
///
/// # Block form
///
/// Expands `let` declarations into the enclosing scope, keeping intermediate providers alive.
/// This supports field-level `#[export]` (references) and multiple declarations (like `lazy_static!`).
///
/// ```rust
/// use nject::{init, injectable, module, provider};
///
/// #[derive(Debug)]
/// #[injectable]
/// struct Secret(#[inject(42)] i32);
///
/// #[injectable]
/// #[module]
/// struct SecretModule {
///     #[export]
///     secret: Secret,
/// }
///
/// #[injectable]
/// struct Consumer<'a>(&'a Secret);
///
/// #[injectable]
/// #[provider]
/// struct AppProvider(#[import] SecretModule);
///
/// init! {
///     let provider: AppProvider = SecretModule;
/// }
/// let consumer: Consumer = provider.provide();
/// assert_eq!(consumer.0.0, 42);
/// ```
#[proc_macro]
pub fn init(item: TokenStream) -> TokenStream {
    handle_init(item).unwrap_or_else(|e| e.to_compile_error().into())
}

/// For internal purposes only. Should not be used.
#[doc(hidden)]
#[proc_macro]
pub fn __nject_finalize_imports(item: TokenStream) -> TokenStream {
    handle_finalize_imports(item).unwrap_or_else(|e| e.to_compile_error().into())
}