use std::sync::Arc;
use tracing::info;
use crate::config::Config;
use crate::error::Result;
use crate::index::PipelineExecutor;
use crate::retrieval::{PipelineRetriever, RetrieveEventReceiver};
use crate::storage::Workspace;
use crate::{DocumentTree, Error};
use super::events::EventEmitter;
use super::index_context::IndexContext;
use super::indexer::IndexerClient;
use super::query_context::QueryContext;
use super::retriever::RetrieverClient;
use super::types::{DocumentInfo, IndexItem, IndexResult, QueryResult};
use super::workspace::WorkspaceClient;
pub struct Engine {
config: Arc<Config>,
indexer: IndexerClient,
retriever: RetrieverClient,
workspace: Option<WorkspaceClient>,
events: EventEmitter,
}
impl Engine {
pub(crate) async fn with_components(
config: Config,
workspace: Workspace,
retriever: PipelineRetriever,
executor: PipelineExecutor,
) -> Result<Self> {
let config = Arc::new(config);
let events = EventEmitter::new();
let indexer = IndexerClient::new(executor).with_events(events.clone());
let retriever =
RetrieverClient::new(retriever, Arc::clone(&config)).with_events(events.clone());
let workspace_client = WorkspaceClient::new(workspace)
.await
.with_events(events.clone());
Ok(Self {
config,
indexer,
retriever,
workspace: Some(workspace_client),
events,
})
}
pub async fn index(&self, ctx: IndexContext) -> Result<IndexResult> {
let doc = self.indexer.index(ctx).await?;
let item = IndexItem::new(doc.id.clone(), doc.name.clone(), doc.format.clone());
let persisted = self.indexer.to_persisted(doc);
if let Some(ref workspace) = self.workspace {
workspace.save(&persisted).await?;
}
info!("Indexed document: {}", item.doc_id);
Ok(IndexResult::new(vec![item]))
}
pub async fn query(&self, ctx: QueryContext) -> Result<QueryResult> {
let doc_id = ctx.doc_id.as_deref().ok_or_else(|| {
Error::Config("doc_id is required for query".to_string())
})?;
let tree = self.get_structure(doc_id).await?;
let options = ctx.to_retrieve_options(&self.config);
let mut result = self.retriever.query(&tree, &ctx.query, &options).await?;
result.doc_id = doc_id.to_string();
Ok(result)
}
pub async fn query_stream(&self, ctx: QueryContext) -> Result<RetrieveEventReceiver> {
let doc_id = ctx.doc_id.as_deref().ok_or_else(|| {
Error::Config("doc_id is required for query".to_string())
})?;
let tree = self.get_structure(doc_id).await?;
let options = ctx.to_retrieve_options(&self.config);
let rx = self.retriever.query_stream(&tree, &ctx.query, &options).await?;
Ok(rx)
}
pub async fn list(&self) -> Result<Vec<DocumentInfo>> {
let workspace = self
.workspace
.as_ref()
.ok_or_else(|| Error::Config("No workspace configured".to_string()))?;
workspace.list().await
}
pub async fn remove(&self, doc_id: &str) -> Result<bool> {
let workspace = self
.workspace
.as_ref()
.ok_or_else(|| Error::Config("No workspace configured".to_string()))?;
workspace.remove(doc_id).await
}
pub async fn exists(&self, doc_id: &str) -> Result<bool> {
let workspace = self
.workspace
.as_ref()
.ok_or_else(|| Error::Config("No workspace configured".to_string()))?;
workspace.exists(doc_id).await
}
pub async fn clear(&self) -> Result<usize> {
let workspace = self
.workspace
.as_ref()
.ok_or_else(|| Error::Config("No workspace configured".to_string()))?;
workspace.clear().await
}
pub(crate) async fn get_structure(&self, doc_id: &str) -> Result<DocumentTree> {
let workspace = self
.workspace
.as_ref()
.ok_or_else(|| Error::Config("No workspace configured".to_string()))?;
let doc = workspace
.load(doc_id)
.await?
.ok_or_else(|| Error::DocumentNotFound(format!("Document not found: {}", doc_id)))?;
Ok(doc.tree)
}
}
impl Clone for Engine {
fn clone(&self) -> Self {
Self {
config: Arc::clone(&self.config),
indexer: self.indexer.clone(),
retriever: self.retriever.clone(),
workspace: self.workspace.clone(),
events: self.events.clone(),
}
}
}
impl std::fmt::Debug for Engine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Engine")
.field("has_workspace", &self.workspace.is_some())
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::super::EngineBuilder;
#[test]
fn test_engine_builder() {
let builder = EngineBuilder::new();
let _ = builder;
}
}