use std::sync::Arc;
use tracing::{debug, info};
use crate::error::Result;
use crate::storage::{PersistedDocument, Workspace};
use super::types::DocumentInfo;
use crate::events::{EventEmitter, WorkspaceEvent};
#[derive(Clone)]
pub(crate) struct WorkspaceClient {
workspace: Arc<Workspace>,
events: EventEmitter,
}
impl WorkspaceClient {
pub async fn new(workspace: Workspace) -> Self {
Self {
workspace: Arc::new(workspace),
events: EventEmitter::new(),
}
}
pub fn with_events(mut self, events: EventEmitter) -> Self {
self.events = events;
self
}
pub async fn save(&self, doc: &PersistedDocument) -> Result<()> {
let doc_id = doc.meta.id.clone();
if self.workspace.contains(&doc_id).await {
tracing::warn!(
doc_id,
name = %doc.meta.name,
"Overwriting existing document — possible concurrent index of the same source"
);
}
self.workspace.add(doc).await?;
info!("Saved document: {}", doc_id);
self.events.emit_workspace(WorkspaceEvent::Saved { doc_id });
Ok(())
}
pub async fn load(&self, doc_id: &str) -> Result<Option<PersistedDocument>> {
let doc = self.workspace.load_and_cache(doc_id).await?;
if let Some(ref _d) = doc {
debug!("Loaded document: {}", doc_id);
}
self.events.emit_workspace(WorkspaceEvent::Loaded {
doc_id: doc_id.to_string(),
cache_hit: doc.is_some(),
});
Ok(doc)
}
pub async fn remove(&self, doc_id: &str) -> Result<bool> {
let removed = self.workspace.remove(doc_id).await?;
if removed {
info!("Removed document: {}", doc_id);
self.events.emit_workspace(WorkspaceEvent::Removed {
doc_id: doc_id.to_string(),
});
}
Ok(removed)
}
pub async fn exists(&self, doc_id: &str) -> Result<bool> {
Ok(self.workspace.contains(doc_id).await)
}
pub async fn list(&self) -> Result<Vec<DocumentInfo>> {
let doc_ids = self.workspace.list_documents().await;
let mut result = Vec::with_capacity(doc_ids.len());
for id in &doc_ids {
if let Some(meta) = self.workspace.get_meta(id).await {
result.push(DocumentInfo {
id: meta.id,
name: meta.doc_name,
format: meta.doc_type,
description: meta.doc_description,
source_path: meta.path,
page_count: meta.page_count,
line_count: meta.line_count,
});
}
}
Ok(result)
}
pub async fn get_document_info(&self, doc_id: &str) -> Result<Option<DocumentInfo>> {
Ok(self
.workspace
.get_meta(doc_id)
.await
.map(|meta| DocumentInfo {
id: meta.id,
name: meta.doc_name,
format: meta.doc_type,
description: meta.doc_description,
source_path: meta.path,
page_count: meta.page_count,
line_count: meta.line_count,
}))
}
pub async fn clear(&self) -> Result<usize> {
let doc_ids = self.workspace.list_documents().await;
let mut removed = 0usize;
for doc_id in &doc_ids {
match self.workspace.remove(doc_id).await {
Ok(true) => removed += 1,
Ok(false) => {}
Err(e) => tracing::warn!("Failed to remove document {}: {}", doc_id, e),
}
}
if removed > 0 {
info!("Cleared workspace: {removed} documents removed");
self.events
.emit_workspace(WorkspaceEvent::Cleared { count: removed });
}
Ok(removed)
}
pub(crate) fn inner(&self) -> Arc<Workspace> {
Arc::clone(&self.workspace)
}
pub async fn find_by_source_path(&self, path: &std::path::Path) -> Option<String> {
self.workspace.find_by_source_path(path).await
}
pub async fn get_graph(&self) -> Result<Option<crate::graph::DocumentGraph>> {
self.workspace.get_graph().await
}
pub async fn set_graph(&self, graph: &crate::graph::DocumentGraph) -> Result<()> {
self.workspace.set_graph(graph).await
}
}