use std::path::PathBuf;
use crate::file_change_event::FileChangeEvent;
use crate::watch_task::WatchTaskIdx;
use crate::watcher_msg::WatcherMsg;
use rolldown_common::WatcherChangeKind;
use rolldown_fs_watcher::{FsEventHandler, FsEventResult};
use tokio::sync::mpsc;
pub struct TaskFsEventHandler {
pub task_index: WatchTaskIdx,
pub tx: mpsc::UnboundedSender<WatcherMsg>,
}
impl TaskFsEventHandler {
fn map_raw_event_kind_to_watcher_event_kind(
kind: ¬ify::EventKind,
) -> Option<WatcherChangeKind> {
match kind {
notify::EventKind::Create(_)
| notify::EventKind::Modify(notify::event::ModifyKind::Name(notify::event::RenameMode::To)) => {
Some(WatcherChangeKind::Create)
}
notify::EventKind::Modify(notify::event::ModifyKind::Name(
notify::event::RenameMode::From,
))
| notify::EventKind::Remove(_) => Some(WatcherChangeKind::Delete),
notify::EventKind::Modify(_) => Some(WatcherChangeKind::Update),
_ => None,
}
}
fn map_rename_to_delete_create_changes(
paths: impl IntoIterator<Item = PathBuf>,
) -> Vec<FileChangeEvent> {
let mut paths = paths.into_iter();
let mut result = Vec::new();
if let Some(from) = paths.next() {
result
.push(FileChangeEvent::new(from.to_string_lossy().into_owned(), WatcherChangeKind::Delete));
}
if let Some(to) = paths.next() {
result
.push(FileChangeEvent::new(to.to_string_lossy().into_owned(), WatcherChangeKind::Create));
}
result
}
}
impl FsEventHandler for TaskFsEventHandler {
fn handle_event(&mut self, event: FsEventResult) {
match event {
Ok(fs_events) => {
let changes: Vec<FileChangeEvent> = fs_events
.into_iter()
.filter_map(|fs_event| {
if matches!(
fs_event.detail.kind,
notify::EventKind::Modify(notify::event::ModifyKind::Name(
notify::event::RenameMode::Both
))
) {
return Some(Self::map_rename_to_delete_create_changes(fs_event.detail.paths));
}
let kind = Self::map_raw_event_kind_to_watcher_event_kind(&fs_event.detail.kind)?;
Some(
fs_event
.detail
.paths
.into_iter()
.map(|path| FileChangeEvent::new(path.to_string_lossy().into_owned(), kind))
.collect(),
)
})
.flatten()
.collect();
if !changes.is_empty() {
let _ = self.tx.send(WatcherMsg::FileChanges { task_index: self.task_index, changes });
}
}
Err(errors) => {
for e in errors {
tracing::error!("notify error: {e:?}");
}
}
}
}
}