noema 0.4.0

Noema IOC and DI framework for Rust
Documentation
/// Register `Type` as a config singleton: `ctor` pushes an async hydrate fn,
/// `load(source)` sets `OnceLock<Arc<Type>>`, later [`resolve`](crate::resolve) clones.
///
/// `Type` must implement [`Configurable`](crate::config::Configurable)
/// (`#[derive(Configurable)]`).
///
/// ```ignore
/// #[derive(Configurable)]
/// struct DatabaseConfig {
///     #[config(name = "DATABASE_URL")]
///     url: String,
/// }
/// config!(DatabaseConfig);
/// ```
#[macro_export]
macro_rules! config {
    ($type:ty) => {
        const _: () = {
            use std::future::Future;
            use std::pin::Pin;
            use std::sync::{Arc, OnceLock};

            use $crate::config::{ConfigError, ConfigSource, Configurable, register};
            use $crate::core::{Container, Resolver};

            static SLOT: OnceLock<Arc<$type>> = OnceLock::new();

            fn __noema_hydrate(
                source: Arc<dyn ConfigSource>,
            ) -> Pin<Box<dyn Future<Output = Result<(), ConfigError>> + Send>> {
                Box::pin(async move {
                    let value = <$type as Configurable>::from_source(&*source).await?;
                    SLOT.set(Arc::new(value))
                        .map_err(|_| ConfigError::AlreadyLoaded(stringify!($type)))?;
                    Ok(())
                })
            }

            #[$crate::ctor::ctor]
            fn __noema_config_register() {
                register(__noema_hydrate);
            }

            impl Resolver<$type> for Container {
                fn resolve() -> Arc<$type> {
                    SLOT.get()
                        .expect("config::load() must run before resolve")
                        .clone()
                }
            }
        };
    };
}