Skip to main content

linkrs_sync/
builder.rs

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}