use terraphim_config::{ConfigState, ServiceType};
use terraphim_types::{Index, SearchQuery};
use crate::{Error, Result};
mod ripgrep;
#[cfg(feature = "ai-assistant")]
use crate::haystack::AiAssistantHaystackIndexer;
#[cfg(feature = "grepapp")]
use crate::haystack::GrepAppHaystackIndexer;
#[cfg(feature = "jmap")]
use crate::haystack::JmapHaystackIndexer;
use crate::haystack::{
ClickUpHaystackIndexer, McpHaystackIndexer, PerplexityHaystackIndexer, QueryRsHaystackIndexer,
QuickwitHaystackIndexer,
};
pub use ripgrep::RipgrepIndexer;
pub trait IndexMiddleware {
fn index(
&self,
needle: &str,
haystack: &terraphim_config::Haystack,
) -> impl std::future::Future<Output = Result<Index>> + Send;
}
pub async fn search_haystacks(
mut config_state: ConfigState,
search_query: SearchQuery,
) -> Result<Index> {
let config = config_state.config.lock().await.clone();
let search_query_role = search_query.role.unwrap_or(config.default_role);
let needle = search_query.search_term.as_str();
let ripgrep = RipgrepIndexer::default();
let query_rs = QueryRsHaystackIndexer::default();
let clickup = ClickUpHaystackIndexer::default();
let mut full_index = Index::new();
let role = config
.roles
.get(&search_query_role)
.ok_or_else(|| Error::RoleNotFound(search_query_role.to_string()))?;
for haystack in &role.haystacks {
log::info!("Finding documents in haystack: {:#?}", haystack);
let index = match haystack.service {
ServiceType::Ripgrep => {
ripgrep.index(needle, haystack).await?
}
ServiceType::Atomic => {
log::warn!(
"Atomic haystack support not enabled. Skipping haystack: {}",
haystack.location
);
Index::new()
}
ServiceType::QueryRs => {
query_rs.index(needle, haystack).await?
}
ServiceType::ClickUp => {
clickup.index(needle, haystack).await?
}
ServiceType::Mcp => {
let mcp = McpHaystackIndexer;
mcp.index(needle, haystack).await?
}
ServiceType::Perplexity => {
let perplexity = match PerplexityHaystackIndexer::from_haystack_config(haystack) {
Ok(indexer) => indexer,
Err(e) => {
log::error!("Failed to create Perplexity indexer: {}", e);
return Ok(Index::new());
}
};
perplexity.index(needle, haystack).await?
}
ServiceType::GrepApp => {
#[cfg(feature = "grepapp")]
{
let grep_app = GrepAppHaystackIndexer::default();
grep_app.index(needle, haystack).await?
}
#[cfg(not(feature = "grepapp"))]
{
log::warn!(
"GrepApp haystack support not enabled. Skipping haystack: {}",
haystack.location
);
Index::new()
}
}
ServiceType::AiAssistant => {
#[cfg(feature = "ai-assistant")]
{
let ai_assistant = AiAssistantHaystackIndexer;
ai_assistant.index(needle, haystack).await?
}
#[cfg(not(feature = "ai-assistant"))]
{
log::warn!(
"AI assistant haystack support not enabled. Skipping haystack: {}",
haystack.location
);
Index::new()
}
}
ServiceType::Quickwit => {
let quickwit = QuickwitHaystackIndexer::default();
quickwit.index(needle, haystack).await?
}
ServiceType::Jmap => {
#[cfg(feature = "jmap")]
{
let jmap = JmapHaystackIndexer;
jmap.index(needle, haystack).await?
}
#[cfg(not(feature = "jmap"))]
{
log::warn!(
"JMAP haystack support not enabled. Skipping haystack: {}",
haystack.location
);
Index::new()
}
}
};
let mut tagged_index = Index::new();
for (doc_id, mut document) in index {
document.source_haystack = Some(haystack.location.clone());
tagged_index.insert(doc_id, document);
}
for indexed_doc in tagged_index.values() {
if let Err(e) = config_state.add_to_roles(indexed_doc).await {
log::warn!(
"Failed to insert document `{}` ({}): {e:?}",
indexed_doc.title,
indexed_doc.url
);
}
}
full_index.extend(tagged_index);
}
Ok(full_index)
}