use fsevent::{Event, StreamFlags};
use std::path::PathBuf;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;
use crate::FileEvent;
struct RenameState {
path: PathBuf,
id: u64,
}
#[derive(Clone)]
pub struct Watcher {
dest: PathBuf,
rename_candidate: Arc<Mutex<Option<RenameState>>>,
}
impl Watcher {
pub fn new(dest: PathBuf) -> Self {
Self {
dest,
rename_candidate: Default::default(),
}
}
pub fn watch_for_changes(&self) -> mpsc::Receiver<FileEvent> {
let (sender, receiver) = mpsc::channel();
let (tx, rx) = mpsc::channel();
let deststr = self.dest.as_os_str().to_str().unwrap().to_string();
thread::spawn(move || {
let fsevent = fsevent::FsEvent::new(vec![deststr]);
fsevent.observe(sender);
});
let event_loop_state = self.clone();
thread::spawn(move || {
event_loop_state.event_loop(receiver, tx);
});
rx
}
fn event_loop(&self, fsevent_rx: Receiver<Event>, tx: Sender<FileEvent>) {
loop {
let val = fsevent_rx.recv().unwrap();
let location = PathBuf::from(&val.path);
let flags = &val.flag;
if flags.contains(StreamFlags::ITEM_CREATED)
&& !flags.contains(StreamFlags::ITEM_REMOVED)
&& !flags.contains(StreamFlags::ITEM_RENAMED)
{
tx.send(FileEvent::Create(location)).unwrap();
} else if flags.contains(StreamFlags::ITEM_REMOVED) {
tx.send(FileEvent::Remove(location)).unwrap();
} else if flags.contains(StreamFlags::ITEM_RENAMED) {
let mut rename_candidate = self.rename_candidate.lock().unwrap();
if let Some(pending) = rename_candidate.take() {
if pending.id == val.event_id - 1 {
if compare_first_parts(pending.path.clone(), location.clone()) {
tx.send(FileEvent::Rename(pending.path, location)).unwrap();
} else if compare_last_parts(pending.path.clone(), location.clone()) {
tx.send(FileEvent::MoveWithin(pending.path, location))
.unwrap();
} else {
tx.send(FileEvent::MoveAndRename(pending.path, location))
.unwrap();
}
} else {
tx.send(FileEvent::MoveOut(pending.path)).unwrap();
tx.send(FileEvent::MoveOut(location)).unwrap();
}
} else {
*rename_candidate = Some(RenameState {
path: location,
id: val.event_id,
});
let clone_self = self.clone();
let clone_tx = tx.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
if let Some(pending) = clone_self.rename_candidate.lock().unwrap().take() {
clone_tx.send(FileEvent::MoveOut(pending.path)).unwrap();
}
});
}
} else if flags.contains(StreamFlags::INODE_META_MOD)
|| flags.contains(StreamFlags::ITEM_MODIFIED)
{
tx.send(FileEvent::Write(location)).unwrap();
}
}
}
}
fn compare_last_parts(path1: PathBuf, path2: PathBuf) -> bool {
return path1.iter().last() == path2.iter().last();
}
fn compare_first_parts(path1: PathBuf, path2: PathBuf) -> bool {
return path1.parent() == path2.parent();
}