use std::path::Path;
use anyhow::{Context, Result};
use tracing::debug;
use crate::db::index_state::UpdateStats;
use crate::db::Store;
use crate::git::get_head_commit;
use crate::incremental::events::{EventType, IndexingEvent};
pub async fn handle_file_event(
store: &(dyn Store + Send + Sync),
worktree_id: i64,
repo: &str,
worktree: &str,
watch_root: &Path,
event: &IndexingEvent,
) -> Result<UpdateStats> {
let relpath = event
.path
.strip_prefix(watch_root)
.unwrap_or(&event.path)
.to_path_buf();
let relpath_str = relpath.to_string_lossy().to_string();
let mut stats = UpdateStats::new();
match event.event_type {
EventType::Modified => {
let abs = if relpath.is_absolute() {
relpath.clone()
} else {
watch_root.join(&relpath)
};
if !abs.is_file() {
debug!(path = %abs.display(), "Modified file no longer exists; awaiting Deleted event");
return Ok(stats);
}
match std::fs::read_to_string(&abs) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
debug!(path = %abs.display(), "file is not valid UTF-8; not indexable (scan parity)");
return Ok(stats);
}
Err(e) => {
return Err(anyhow::anyhow!(e)
.context(format!("file exists but is unreadable: {}", abs.display())));
}
}
let commit = get_head_commit(watch_root)?;
crate::indexer::upsert_files(
store,
repo,
worktree,
watch_root,
&commit,
std::slice::from_ref(&relpath),
)
.await
.with_context(|| format!("failed to upsert {}", relpath_str))?;
stats.files_processed = 1;
}
EventType::Renamed => {
if let Some(old) = &event.old_path {
let old_rel = old
.strip_prefix(watch_root)
.unwrap_or(old)
.to_string_lossy()
.to_string();
store
.unmap_superseded_file_chunks(worktree_id, &old_rel, None)
.await?;
stats.files_processed += 1;
}
let abs = if relpath.is_absolute() {
relpath.clone()
} else {
watch_root.join(&relpath)
};
if abs.is_file() {
std::fs::File::open(&abs).with_context(|| {
format!("file exists but is unreadable: {}", abs.display())
})?;
let commit = get_head_commit(watch_root)?;
crate::indexer::upsert_files(
store,
repo,
worktree,
watch_root,
&commit,
std::slice::from_ref(&relpath),
)
.await
.with_context(|| format!("failed to upsert renamed {}", relpath_str))?;
stats.files_processed += 1;
} else {
debug!(path = %abs.display(), "Renamed target no longer exists; awaiting Deleted event");
}
}
EventType::Deleted => {
let abs = if relpath.is_absolute() {
relpath.clone()
} else {
watch_root.join(&relpath)
};
if abs.exists() {
debug!(
path = %abs.display(),
"Deleted event but file exists on disk (left git-status via commit/stash/restore); re-upserting instead of unmapping"
);
if abs.is_file() {
std::fs::File::open(&abs).with_context(|| {
format!("file exists but is unreadable: {}", abs.display())
})?;
let commit = get_head_commit(watch_root)?;
crate::indexer::upsert_files(
store,
repo,
worktree,
watch_root,
&commit,
std::slice::from_ref(&relpath),
)
.await
.with_context(|| format!("failed to re-upsert {}", relpath_str))?;
stats.files_processed = 1;
}
return Ok(stats);
}
let affected = store
.unmap_superseded_file_chunks(worktree_id, &relpath_str, None)
.await?;
debug!(
file = %relpath_str,
junction_rows = affected,
"Unmapped worktree from chunks for deleted file"
);
stats.files_processed = 1;
}
}
Ok(stats)
}