use async_trait::async_trait;
use std::path::Path;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::debug;
use crate::clangd::file_manager::ClangdFileManager;
use crate::clangd::session::{ClangdSession, ClangdSessionTrait};
use crate::lsp::traits::LspClientTrait;
use crate::project::ProjectError;
#[cfg_attr(test, mockall::automock)]
#[async_trait]
pub trait IndexTrigger: Send + Sync {
async fn trigger(&self, file_path: &Path) -> Result<(), ProjectError>;
}
pub struct ClangdIndexTrigger {
session: Arc<Mutex<ClangdSession>>,
file_manager: Arc<Mutex<ClangdFileManager>>,
}
impl ClangdIndexTrigger {
pub fn new(
session: Arc<Mutex<ClangdSession>>,
file_manager: Arc<Mutex<ClangdFileManager>>,
) -> Self {
debug!("Created ClangdIndexTrigger");
Self {
session,
file_manager,
}
}
}
#[async_trait]
impl IndexTrigger for ClangdIndexTrigger {
async fn trigger(&self, file_path: &Path) -> Result<(), ProjectError> {
debug!("Triggering indexing for file: {:?}", file_path);
let mut session = self.session.lock().await;
let mut file_manager = self.file_manager.lock().await;
file_manager
.ensure_file_ready(file_path, session.client_mut())
.await
.map_err(|e| {
ProjectError::IndexingTrigger(format!(
"Failed to ensure file ready for indexing {}: {}",
file_path.display(),
e
))
})?;
let file_uri = crate::symbol::uri_from_pathbuf(file_path);
debug!("Requesting document symbols for file: {:?}", file_path);
let client = session.client_mut();
match client.text_document_document_symbol(file_uri).await {
Ok(_) => {
debug!(
"Successfully retrieved document symbols for file: {:?}",
file_path
);
}
Err(e) => {
debug!(
"Failed to retrieve document symbols for {}: {} (continuing anyway)",
file_path.display(),
e
);
}
}
debug!("Successfully triggered indexing for file: {:?}", file_path);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[tokio::test]
async fn test_mock_index_trigger() {
let mut mock_trigger = MockIndexTrigger::new();
let test_path = PathBuf::from("/test/file.cpp");
let expected_path = test_path.clone();
mock_trigger
.expect_trigger()
.with(mockall::predicate::function(move |path: &Path| {
path == expected_path
}))
.times(1)
.returning(|_| Ok(()));
let result = mock_trigger.trigger(&test_path).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_mock_index_trigger_failure() {
let mut mock_trigger = MockIndexTrigger::new();
let test_path = PathBuf::from("/test/file.cpp");
let expected_path = test_path.clone();
mock_trigger
.expect_trigger()
.with(mockall::predicate::function(move |path: &Path| {
path == expected_path
}))
.times(1)
.returning(|_| Err(ProjectError::IndexingTrigger("Test error".to_string())));
let result = mock_trigger.trigger(&test_path).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Test error"));
}
}