1use std::sync::Arc;
2
3#[cfg(feature = "fulltext")]
4use crate::coordinator::SyncCoordinator;
5#[cfg(feature = "vector")]
6use crate::vector_sync::VectorSyncCoordinator;
7use crate::DeadLetterQueue;
8use crate::SyncManager;
9
10pub struct SyncManagerBuilder {
11 #[cfg(feature = "fulltext")]
12 sync_coordinator: Option<Arc<SyncCoordinator>>,
13 #[cfg(feature = "vector")]
14 vector_coordinator: Option<Arc<VectorSyncCoordinator>>,
15 dead_letter_queue: Option<Arc<DeadLetterQueue>>,
16 outbox_path: Option<std::path::PathBuf>,
17 #[cfg(feature = "vector")]
18 auto_embed_text: bool,
19}
20
21impl Default for SyncManagerBuilder {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl SyncManagerBuilder {
28 pub fn new() -> Self {
29 Self {
30 #[cfg(feature = "fulltext")]
31 sync_coordinator: None,
32 #[cfg(feature = "vector")]
33 vector_coordinator: None,
34 dead_letter_queue: None,
35 outbox_path: None,
36 #[cfg(feature = "vector")]
37 auto_embed_text: false,
38 }
39 }
40
41 #[cfg(feature = "fulltext")]
42 pub fn with_sync_coordinator(mut self, coordinator: Arc<SyncCoordinator>) -> Self {
43 self.sync_coordinator = Some(coordinator);
44 self
45 }
46
47 #[cfg(feature = "vector")]
48 pub fn with_vector_coordinator(mut self, coordinator: Arc<VectorSyncCoordinator>) -> Self {
49 self.vector_coordinator = Some(coordinator);
50 self
51 }
52
53 pub fn with_dead_letter_queue(mut self, dlq: Arc<DeadLetterQueue>) -> Self {
54 self.dead_letter_queue = Some(dlq);
55 self
56 }
57
58 pub fn with_outbox_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
59 self.outbox_path = Some(path.into());
60 self
61 }
62
63 #[cfg(feature = "vector")]
64 pub fn with_auto_embed_text(mut self, enabled: bool) -> Self {
65 self.auto_embed_text = enabled;
66 self
67 }
68
69 pub fn build(self) -> Result<SyncManager, crate::manager::SyncError> {
70 let mut manager = SyncManager::new_without_fulltext();
71
72 #[cfg(feature = "fulltext")]
73 if let Some(coordinator) = self.sync_coordinator {
74 manager = SyncManager::new(coordinator);
75 }
76
77 #[cfg(feature = "vector")]
78 if let Some(vector_coordinator) = self.vector_coordinator {
79 manager = manager.with_vector_coordinator(vector_coordinator);
80 }
81
82 if let Some(dlq) = self.dead_letter_queue {
83 manager = manager.with_dead_letter_queue(dlq);
84 }
85
86 if let Some(path) = self.outbox_path {
87 manager.configure_outbox(path)?;
88 }
89
90 #[cfg(feature = "vector")]
91 manager.set_auto_embed_text(self.auto_embed_text);
92
93 Ok(manager)
94 }
95}
96
97#[cfg(feature = "fulltext")]
98pub struct SyncCoordinatorBuilder {
99 fulltext_manager: Option<Arc<linkrs_fulltext::manager::FulltextIndexManager>>,
100 config: Option<crate::batch::BatchConfig>,
101 stats_manager: Option<Arc<linkrs_metrics::StatsManager>>,
102}
103
104#[cfg(feature = "fulltext")]
105impl Default for SyncCoordinatorBuilder {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[cfg(feature = "fulltext")]
112impl SyncCoordinatorBuilder {
113 pub fn new() -> Self {
114 Self {
115 fulltext_manager: None,
116 config: None,
117 stats_manager: None,
118 }
119 }
120
121 pub fn with_fulltext_manager(
122 mut self,
123 manager: Arc<linkrs_fulltext::manager::FulltextIndexManager>,
124 ) -> Self {
125 self.fulltext_manager = Some(manager);
126 self
127 }
128
129 pub fn with_config(mut self, config: crate::batch::BatchConfig) -> Self {
130 self.config = Some(config);
131 self
132 }
133
134 pub fn with_stats_manager(mut self, stats_manager: Arc<linkrs_metrics::StatsManager>) -> Self {
135 self.stats_manager = Some(stats_manager);
136 self
137 }
138
139 pub fn build(self) -> Result<Arc<SyncCoordinator>, crate::SyncError> {
140 let manager = self.fulltext_manager.ok_or_else(|| {
141 crate::SyncError::Internal("FulltextIndexManager is required".to_string())
142 })?;
143 let config = self.config.unwrap_or_default();
144
145 let mut coordinator = SyncCoordinator::new(manager, config);
146
147 if let Some(stats) = self.stats_manager {
148 coordinator = coordinator.with_stats_manager(stats);
149 }
150
151 Ok(Arc::new(coordinator))
152 }
153}
154
155#[cfg(feature = "vector")]
156pub struct VectorCoordinatorBuilder {
157 backend: Option<crate::backend::VectorBackend>,
158 #[cfg(feature = "vector-qdrant")]
159 vector_manager: Option<Arc<vector_client::VectorManager>>,
160 #[cfg(feature = "embedding")]
161 embedding_service: Option<Arc<crate::vector_sync::EmbeddingService>>,
162 runtime_handle: Option<tokio::runtime::Handle>,
163}
164
165#[cfg(feature = "vector")]
166impl Default for VectorCoordinatorBuilder {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172#[cfg(feature = "vector")]
173impl VectorCoordinatorBuilder {
174 pub fn new() -> Self {
175 Self {
176 backend: None,
177 #[cfg(feature = "vector-qdrant")]
178 vector_manager: None,
179 #[cfg(feature = "embedding")]
180 embedding_service: None,
181 runtime_handle: None,
182 }
183 }
184
185 pub fn with_backend(mut self, backend: crate::backend::VectorBackend) -> Self {
186 self.backend = Some(backend);
187 self
188 }
189
190 #[cfg(feature = "vector-qdrant")]
191 pub fn with_vector_manager(mut self, manager: Arc<vector_client::VectorManager>) -> Self {
192 self.vector_manager = Some(manager);
193 self
194 }
195
196 #[cfg(feature = "embedding")]
197 pub fn with_embedding_service(
198 mut self,
199 service: Arc<crate::vector_sync::EmbeddingService>,
200 ) -> Self {
201 self.embedding_service = Some(service);
202 self
203 }
204
205 pub fn with_runtime_handle(mut self, handle: tokio::runtime::Handle) -> Self {
206 self.runtime_handle = Some(handle);
207 self
208 }
209
210 pub fn build(self) -> Result<Arc<VectorSyncCoordinator>, crate::SyncError> {
211 let backend = if let Some(backend) = self.backend {
212 backend
213 } else {
214 #[cfg(feature = "vector-qdrant")]
215 if let Some(manager) = self.vector_manager {
216 crate::backend::VectorBackend::qdrant(manager)
217 } else {
218 return Err(crate::SyncError::Internal(
219 "VectorBackend or VectorManager is required".to_string(),
220 ));
221 }
222 #[cfg(not(feature = "vector-qdrant"))]
223 return Err(crate::SyncError::Internal(
224 "VectorBackend is required".to_string(),
225 ));
226 };
227 let handle = self.runtime_handle.unwrap_or_else(|| {
228 tokio::runtime::Handle::try_current().expect("No tokio runtime available")
229 });
230 #[cfg(feature = "embedding")]
231 {
232 Ok(Arc::new(VectorSyncCoordinator::new(
233 backend,
234 self.embedding_service,
235 handle,
236 )))
237 }
238 #[cfg(not(feature = "embedding"))]
239 {
240 Ok(Arc::new(VectorSyncCoordinator::new(backend, handle)))
241 }
242 }
243}