srusty-files 0.2.0

A high-performance, cross-platform file search engine library with REST API
Documentation
use crate::core::config::SearchConfig;
use crate::core::error::Result;
use crate::filters::ExclusionFilter;
use crate::indexer::incremental::IncrementalIndexer;
use crate::storage::Database;
use crate::watcher::debouncer::FileEventType;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::mpsc;

#[derive(Clone, Debug)]
pub struct FileEvent {
    pub path: PathBuf,
    pub event_type: FileEventType,
}

pub struct IndexSynchronizer {
    indexer: Arc<IncrementalIndexer>,
    event_receiver: Option<mpsc::UnboundedReceiver<FileEvent>>,
    event_sender: mpsc::UnboundedSender<FileEvent>,
}

impl IndexSynchronizer {
    pub fn new(
        database: Arc<Database>,
        config: Arc<SearchConfig>,
        exclusion_filter: Arc<ExclusionFilter>,
    ) -> Self {
        let (sender, receiver) = mpsc::unbounded_channel();

        let indexer = Arc::new(IncrementalIndexer::new(database, config, exclusion_filter));

        Self {
            indexer,
            event_receiver: Some(receiver),
            event_sender: sender,
        }
    }

    pub fn get_sender(&self) -> mpsc::UnboundedSender<FileEvent> {
        self.event_sender.clone()
    }

    pub async fn start(&mut self) -> Result<()> {
        let mut receiver = self.event_receiver.take().ok_or_else(|| {
            crate::core::error::SearchError::NotInitialized(
                "Synchronizer already started".to_string(),
            )
        })?;

        while let Some(event) = receiver.recv().await {
            if let Err(e) = self.handle_event(event).await {
                log::error!("Failed to handle file event: {}", e);
            }
        }

        Ok(())
    }

    async fn handle_event(&self, event: FileEvent) -> Result<()> {
        match event.event_type {
            FileEventType::Created | FileEventType::Modified => {
                self.indexer.update_file(&event.path)?;
            }
            FileEventType::Deleted => {
                self.indexer
                    .update_file(&event.path)?;
            }
            FileEventType::Renamed => {
                self.indexer.update_file(&event.path)?;
            }
        }

        Ok(())
    }

    pub fn sync_path(&self, path: PathBuf) -> Result<()> {
        self.indexer.update_file(path)?;
        Ok(())
    }

    pub fn sync_paths(&self, paths: Vec<PathBuf>) -> Result<usize> {
        self.indexer.update_files(&paths)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::config::SearchConfig;
    use std::fs;
    use tempfile::TempDir;

    #[tokio::test]
    async fn test_synchronizer() {
        let temp_dir = TempDir::new().unwrap();
        let file_path = temp_dir.path().join("test.txt");

        fs::write(&file_path, "content").unwrap();

        let db = Arc::new(Database::in_memory(10).unwrap());
        let config = Arc::new(SearchConfig::default());
        let filter = Arc::new(ExclusionFilter::default());

        let synchronizer = IndexSynchronizer::new(db, config, filter);

        let result = synchronizer.sync_path(file_path);
        assert!(result.is_ok());
    }
}