linkrs-sync 0.1.0

Synchronization primitives for Linkrs
Documentation
use std::sync::Arc;

#[cfg(feature = "fulltext")]
use crate::coordinator::SyncCoordinator;
#[cfg(feature = "vector")]
use crate::vector_sync::VectorSyncCoordinator;
use crate::DeadLetterQueue;
use crate::SyncManager;

pub struct SyncManagerBuilder {
    #[cfg(feature = "fulltext")]
    sync_coordinator: Option<Arc<SyncCoordinator>>,
    #[cfg(feature = "vector")]
    vector_coordinator: Option<Arc<VectorSyncCoordinator>>,
    dead_letter_queue: Option<Arc<DeadLetterQueue>>,
    outbox_path: Option<std::path::PathBuf>,
    #[cfg(feature = "vector")]
    auto_embed_text: bool,
}

impl Default for SyncManagerBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl SyncManagerBuilder {
    pub fn new() -> Self {
        Self {
            #[cfg(feature = "fulltext")]
            sync_coordinator: None,
            #[cfg(feature = "vector")]
            vector_coordinator: None,
            dead_letter_queue: None,
            outbox_path: None,
            #[cfg(feature = "vector")]
            auto_embed_text: false,
        }
    }

    #[cfg(feature = "fulltext")]
    pub fn with_sync_coordinator(mut self, coordinator: Arc<SyncCoordinator>) -> Self {
        self.sync_coordinator = Some(coordinator);
        self
    }

    #[cfg(feature = "vector")]
    pub fn with_vector_coordinator(mut self, coordinator: Arc<VectorSyncCoordinator>) -> Self {
        self.vector_coordinator = Some(coordinator);
        self
    }

    pub fn with_dead_letter_queue(mut self, dlq: Arc<DeadLetterQueue>) -> Self {
        self.dead_letter_queue = Some(dlq);
        self
    }

    pub fn with_outbox_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
        self.outbox_path = Some(path.into());
        self
    }

    #[cfg(feature = "vector")]
    pub fn with_auto_embed_text(mut self, enabled: bool) -> Self {
        self.auto_embed_text = enabled;
        self
    }

    pub fn build(self) -> Result<SyncManager, crate::manager::SyncError> {
        let mut manager = SyncManager::new_without_fulltext();

        #[cfg(feature = "fulltext")]
        if let Some(coordinator) = self.sync_coordinator {
            manager = SyncManager::new(coordinator);
        }

        #[cfg(feature = "vector")]
        if let Some(vector_coordinator) = self.vector_coordinator {
            manager = manager.with_vector_coordinator(vector_coordinator);
        }

        if let Some(dlq) = self.dead_letter_queue {
            manager = manager.with_dead_letter_queue(dlq);
        }

        if let Some(path) = self.outbox_path {
            manager.configure_outbox(path)?;
        }

        #[cfg(feature = "vector")]
        manager.set_auto_embed_text(self.auto_embed_text);

        Ok(manager)
    }
}

#[cfg(feature = "fulltext")]
pub struct SyncCoordinatorBuilder {
    fulltext_manager: Option<Arc<linkrs_fulltext::manager::FulltextIndexManager>>,
    config: Option<crate::batch::BatchConfig>,
    stats_manager: Option<Arc<linkrs_metrics::StatsManager>>,
}

#[cfg(feature = "fulltext")]
impl Default for SyncCoordinatorBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "fulltext")]
impl SyncCoordinatorBuilder {
    pub fn new() -> Self {
        Self {
            fulltext_manager: None,
            config: None,
            stats_manager: None,
        }
    }

    pub fn with_fulltext_manager(
        mut self,
        manager: Arc<linkrs_fulltext::manager::FulltextIndexManager>,
    ) -> Self {
        self.fulltext_manager = Some(manager);
        self
    }

    pub fn with_config(mut self, config: crate::batch::BatchConfig) -> Self {
        self.config = Some(config);
        self
    }

    pub fn with_stats_manager(mut self, stats_manager: Arc<linkrs_metrics::StatsManager>) -> Self {
        self.stats_manager = Some(stats_manager);
        self
    }

    pub fn build(self) -> Result<Arc<SyncCoordinator>, crate::SyncError> {
        let manager = self.fulltext_manager.ok_or_else(|| {
            crate::SyncError::Internal("FulltextIndexManager is required".to_string())
        })?;
        let config = self.config.unwrap_or_default();

        let mut coordinator = SyncCoordinator::new(manager, config);

        if let Some(stats) = self.stats_manager {
            coordinator = coordinator.with_stats_manager(stats);
        }

        Ok(Arc::new(coordinator))
    }
}

#[cfg(feature = "vector")]
pub struct VectorCoordinatorBuilder {
    backend: Option<crate::backend::VectorBackend>,
    #[cfg(feature = "vector-qdrant")]
    vector_manager: Option<Arc<vector_client::VectorManager>>,
    #[cfg(feature = "embedding")]
    embedding_service: Option<Arc<crate::vector_sync::EmbeddingService>>,
    runtime_handle: Option<tokio::runtime::Handle>,
}

#[cfg(feature = "vector")]
impl Default for VectorCoordinatorBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "vector")]
impl VectorCoordinatorBuilder {
    pub fn new() -> Self {
        Self {
            backend: None,
            #[cfg(feature = "vector-qdrant")]
            vector_manager: None,
            #[cfg(feature = "embedding")]
            embedding_service: None,
            runtime_handle: None,
        }
    }

    pub fn with_backend(mut self, backend: crate::backend::VectorBackend) -> Self {
        self.backend = Some(backend);
        self
    }

    #[cfg(feature = "vector-qdrant")]
    pub fn with_vector_manager(mut self, manager: Arc<vector_client::VectorManager>) -> Self {
        self.vector_manager = Some(manager);
        self
    }

    #[cfg(feature = "embedding")]
    pub fn with_embedding_service(
        mut self,
        service: Arc<crate::vector_sync::EmbeddingService>,
    ) -> Self {
        self.embedding_service = Some(service);
        self
    }

    pub fn with_runtime_handle(mut self, handle: tokio::runtime::Handle) -> Self {
        self.runtime_handle = Some(handle);
        self
    }

    pub fn build(self) -> Result<Arc<VectorSyncCoordinator>, crate::SyncError> {
        let backend = if let Some(backend) = self.backend {
            backend
        } else {
            #[cfg(feature = "vector-qdrant")]
            if let Some(manager) = self.vector_manager {
                crate::backend::VectorBackend::qdrant(manager)
            } else {
                return Err(crate::SyncError::Internal(
                    "VectorBackend or VectorManager is required".to_string(),
                ));
            }
            #[cfg(not(feature = "vector-qdrant"))]
            return Err(crate::SyncError::Internal(
                "VectorBackend is required".to_string(),
            ));
        };
        let handle = self.runtime_handle.unwrap_or_else(|| {
            tokio::runtime::Handle::try_current().expect("No tokio runtime available")
        });
        #[cfg(feature = "embedding")]
        {
            Ok(Arc::new(VectorSyncCoordinator::new(
                backend,
                self.embedding_service,
                handle,
            )))
        }
        #[cfg(not(feature = "embedding"))]
        {
            Ok(Arc::new(VectorSyncCoordinator::new(backend, handle)))
        }
    }
}