use crate::container::{Container, ServiceRegistry, ServiceScope};
use crate::errors::CoreError;
use crate::foundation::traits::Service;
pub struct ContainerBuilder {
registry: ServiceRegistry,
scope: ServiceScope,
}
impl ContainerBuilder {
pub fn new() -> Self {
Self {
registry: ServiceRegistry::new(),
scope: ServiceScope::Singleton,
}
}
pub fn with_scope(mut self, scope: ServiceScope) -> Self {
self.scope = scope;
self
}
pub fn add_service<T>(mut self, service: T) -> Result<Self, CoreError>
where
T: Service + Clone + 'static,
{
self.registry.register_service(service)?;
Ok(self)
}
pub fn add_singleton<T>(mut self, service: T) -> Result<Self, CoreError>
where
T: Service + Clone + 'static,
{
self.registry.register_singleton(service)?;
Ok(self)
}
pub fn add_transient<T>(
mut self,
factory: Box<dyn Fn() -> T + Send + Sync>,
) -> Result<Self, CoreError>
where
T: Service + 'static,
{
self.registry.register_transient(factory)?;
Ok(self)
}
pub fn add_services<T>(mut self, services: Vec<T>) -> Result<Self, CoreError>
where
T: Service + Clone + 'static,
{
for service in services {
self.registry.register_service(service)?;
}
Ok(self)
}
pub fn configure<F>(self, configure: F) -> Result<Self, CoreError>
where
F: FnOnce(Self) -> Result<Self, CoreError>,
{
configure(self)
}
pub fn build(self) -> Result<Container, CoreError> {
let container = Container::new();
container.validate()?;
Ok(container)
}
pub async fn build_and_initialize(self) -> Result<Container, CoreError> {
let mut container = self.build()?;
container.initialize().await?;
Ok(container)
}
}
impl Default for ContainerBuilder {
fn default() -> Self {
Self::new()
}
}
#[macro_export]
macro_rules! container {
($($service:expr),* $(,)?) => {
{
let mut builder = $crate::container::ContainerBuilder::new();
$(
builder = builder.add_service($service)?;
)*
builder.build()
}
};
}
#[macro_export]
macro_rules! singleton_container {
($($service:expr),* $(,)?) => {
{
let mut builder = $crate::container::ContainerBuilder::new()
.with_scope($crate::container::ServiceScope::Singleton);
$(
builder = builder.add_singleton($service)?;
)*
builder.build()
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::foundation::traits::{FrameworkComponent, Service};
#[derive(Clone, Debug)]
#[allow(dead_code)]
struct TestService {
name: String,
}
impl FrameworkComponent for TestService {}
impl Service for TestService {}
impl TestService {
fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
}
#[tokio::test]
async fn test_container_builder() -> Result<(), CoreError> {
let container = ContainerBuilder::new()
.add_service(TestService::new("test1"))?
.add_singleton(TestService::new("test2"))?
.build_and_initialize()
.await?;
assert!(container.is_initialized());
Ok(())
}
}