Skip to main content

allsource_core/infrastructure/di/
builder.rs

1//! Container Builder - Fluent API for constructing ServiceContainer
2//!
3//! Provides a builder pattern for wiring up repositories and creating
4//! a fully configured ServiceContainer.
5
6use super::ServiceContainer;
7use crate::domain::repositories::{
8    AccessTokenRepository, ArticleRepository, CreatorRepository, EventStreamRepository,
9    ForkRepository, TransactionRepository,
10};
11use crate::infrastructure::repositories::{
12    InMemoryAccessTokenRepository, InMemoryArticleRepository, InMemoryCreatorRepository,
13    InMemoryEventStreamRepository, InMemoryForkRepository, InMemoryTransactionRepository,
14};
15use std::sync::Arc;
16
17/// Builder for constructing a ServiceContainer with all dependencies.
18///
19/// # Example
20///
21/// ```rust,ignore
22/// // Development with in-memory repositories
23/// let container = ContainerBuilder::new()
24///     .with_in_memory_repositories()
25///     .build();
26///
27/// // Testing with custom mock repositories
28/// let container = ContainerBuilder::new()
29///     .with_creator_repository(Arc::new(mock_creator_repo))
30///     .with_article_repository(Arc::new(mock_article_repo))
31///     .with_transaction_repository(Arc::new(mock_transaction_repo))
32///     .with_access_token_repository(Arc::new(mock_access_repo))
33///     .with_fork_repository(Arc::new(mock_fork_repo))
34///     .with_event_stream_repository(Arc::new(mock_event_stream_repo))
35///     .build();
36/// ```
37#[derive(Default)]
38pub struct ContainerBuilder {
39    creator_repository: Option<Arc<dyn CreatorRepository>>,
40    article_repository: Option<Arc<dyn ArticleRepository>>,
41    transaction_repository: Option<Arc<dyn TransactionRepository>>,
42    access_token_repository: Option<Arc<dyn AccessTokenRepository>>,
43    fork_repository: Option<Arc<dyn ForkRepository>>,
44    event_stream_repository: Option<Arc<dyn EventStreamRepository>>,
45}
46
47impl ContainerBuilder {
48    /// Create a new empty builder.
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    /// Configure all repositories with in-memory implementations.
54    ///
55    /// This is the recommended setup for development and testing.
56    /// For production, use specific repository setters with persistent implementations.
57    pub fn with_in_memory_repositories(mut self) -> Self {
58        self.creator_repository = Some(Arc::new(InMemoryCreatorRepository::new()));
59        self.article_repository = Some(Arc::new(InMemoryArticleRepository::new()));
60        self.transaction_repository = Some(Arc::new(InMemoryTransactionRepository::new()));
61        self.access_token_repository = Some(Arc::new(InMemoryAccessTokenRepository::new()));
62        self.fork_repository = Some(Arc::new(InMemoryForkRepository::new()));
63        self.event_stream_repository = Some(Arc::new(InMemoryEventStreamRepository::new()));
64        self
65    }
66
67    /// Set a custom creator repository.
68    pub fn with_creator_repository(
69        mut self,
70        repository: Arc<dyn CreatorRepository>,
71    ) -> Self {
72        self.creator_repository = Some(repository);
73        self
74    }
75
76    /// Set a custom article repository.
77    pub fn with_article_repository(
78        mut self,
79        repository: Arc<dyn ArticleRepository>,
80    ) -> Self {
81        self.article_repository = Some(repository);
82        self
83    }
84
85    /// Set a custom transaction repository.
86    pub fn with_transaction_repository(
87        mut self,
88        repository: Arc<dyn TransactionRepository>,
89    ) -> Self {
90        self.transaction_repository = Some(repository);
91        self
92    }
93
94    /// Set a custom access token repository.
95    pub fn with_access_token_repository(
96        mut self,
97        repository: Arc<dyn AccessTokenRepository>,
98    ) -> Self {
99        self.access_token_repository = Some(repository);
100        self
101    }
102
103    /// Set a custom fork repository.
104    pub fn with_fork_repository(
105        mut self,
106        repository: Arc<dyn ForkRepository>,
107    ) -> Self {
108        self.fork_repository = Some(repository);
109        self
110    }
111
112    /// Set a custom event stream repository.
113    pub fn with_event_stream_repository(
114        mut self,
115        repository: Arc<dyn EventStreamRepository>,
116    ) -> Self {
117        self.event_stream_repository = Some(repository);
118        self
119    }
120
121    /// Build the ServiceContainer.
122    ///
123    /// # Panics
124    ///
125    /// Panics if any required repository has not been set.
126    /// Use `with_in_memory_repositories()` to set all repositories at once,
127    /// or set each one individually.
128    pub fn build(self) -> ServiceContainer {
129        ServiceContainer::new(
130            self.creator_repository
131                .expect("CreatorRepository not configured. Use with_in_memory_repositories() or with_creator_repository()"),
132            self.article_repository
133                .expect("ArticleRepository not configured. Use with_in_memory_repositories() or with_article_repository()"),
134            self.transaction_repository
135                .expect("TransactionRepository not configured. Use with_in_memory_repositories() or with_transaction_repository()"),
136            self.access_token_repository
137                .expect("AccessTokenRepository not configured. Use with_in_memory_repositories() or with_access_token_repository()"),
138            self.fork_repository
139                .expect("ForkRepository not configured. Use with_in_memory_repositories() or with_fork_repository()"),
140            self.event_stream_repository
141                .expect("EventStreamRepository not configured. Use with_in_memory_repositories() or with_event_stream_repository()"),
142        )
143    }
144
145    /// Try to build the ServiceContainer, returning an error if any repository is missing.
146    ///
147    /// This is a safer alternative to `build()` that doesn't panic.
148    pub fn try_build(self) -> Result<ServiceContainer, ContainerBuilderError> {
149        Ok(ServiceContainer::new(
150            self.creator_repository
151                .ok_or(ContainerBuilderError::MissingRepository("CreatorRepository"))?,
152            self.article_repository
153                .ok_or(ContainerBuilderError::MissingRepository("ArticleRepository"))?,
154            self.transaction_repository
155                .ok_or(ContainerBuilderError::MissingRepository("TransactionRepository"))?,
156            self.access_token_repository
157                .ok_or(ContainerBuilderError::MissingRepository("AccessTokenRepository"))?,
158            self.fork_repository
159                .ok_or(ContainerBuilderError::MissingRepository("ForkRepository"))?,
160            self.event_stream_repository
161                .ok_or(ContainerBuilderError::MissingRepository("EventStreamRepository"))?,
162        ))
163    }
164}
165
166/// Error returned when building a container fails.
167#[derive(Debug, Clone)]
168pub enum ContainerBuilderError {
169    /// A required repository was not configured.
170    MissingRepository(&'static str),
171}
172
173impl std::fmt::Display for ContainerBuilderError {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        match self {
176            ContainerBuilderError::MissingRepository(name) => {
177                write!(f, "{} not configured", name)
178            }
179        }
180    }
181}
182
183impl std::error::Error for ContainerBuilderError {}