pub trait Configurable: Default + Send + Sync {
fn configure(&mut self);
}
#[macro_export]
macro_rules! configs {
($($item:tt)*) => {
$crate::__configs_internal!($($item)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __configs_internal {
() => {};
($t:ty, $($rest:tt)*) => {
const _: () = {
use $crate::configuration::Configurable;
use $crate::resolver::{Resolver, Container};
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<$t>> = LazyLock::new(|| {
let mut value = <$t>::default();
value.configure();
Arc::new(value)
});
impl Resolver<$t> for Container {
fn resolve() -> Arc<$t> {
INSTANCE.clone()
}
}
};
$crate::__configs_internal!($($rest)*);
};
($t:ty) => {
const _: () = {
use $crate::configuration::Configurable;
use $crate::resolver::{Resolver, Container};
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<$t>> = LazyLock::new(|| {
let mut value = <$t>::default();
value.configure();
Arc::new(value)
});
impl Resolver<$t> for Container {
fn resolve() -> Arc<$t> {
INSTANCE.clone()
}
}
};
};
}
#[cfg(test)]
mod test {
#[test]
fn config_test() {
#[derive(Default)]
struct TestConfig {
pub field1: String,
pub field2: u32,
}
impl crate::configuration::Configurable for TestConfig {
fn configure(&mut self) {
self.field1 = "TestValue".to_string();
self.field2 = 100;
}
}
#[derive(Default)]
struct AnotherConfig {
pub flag: bool,
}
impl crate::configuration::Configurable for AnotherConfig {
fn configure(&mut self) {
self.flag = true;
}
}
configs!(TestConfig, AnotherConfig);
let config = crate::resolver::resolve::<TestConfig>();
let another_config = crate::resolver::resolve::<AnotherConfig>();
assert_eq!(config.field1, "TestValue");
assert_eq!(config.field2, 100);
assert!(another_config.flag);
}
}