use crate::walker::WalkerActor;
use actix::{Actor, Addr, Handler, Message, SyncContext};
#[cfg(target_os = "linux")]
use notify::event::{AccessKind, AccessMode};
use notify::{Event, EventKind};
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::path::{Path, PathBuf};
#[derive(Message)]
#[rtype(result = "()")]
pub struct StartWatching(pub String);
#[derive(Message)]
#[rtype(result = "()")]
pub struct StopWatching(pub String);
#[derive(Message)]
#[rtype(result = "()")]
pub struct FileCreated(pub PathBuf);
pub struct NotifierActor {
watcher: Option<RecommendedWatcher>,
}
impl Actor for NotifierActor {
type Context = SyncContext<Self>;
}
impl Handler<StartWatching> for NotifierActor {
type Result = ();
fn handle(&mut self, msg: StartWatching, _: &mut Self::Context) -> Self::Result {
if let Some(watcher) = &mut self.watcher {
match watcher.watch(Path::new(&msg.0), RecursiveMode::Recursive) {
Ok(_) => info!("Watcher is set recursively to {}", msg.0),
Err(e) => error!("Could not set watcher for {}: {}", msg.0, e),
}
}
}
}
impl Handler<StopWatching> for NotifierActor {
type Result = ();
fn handle(&mut self, msg: StopWatching, _: &mut Self::Context) -> Self::Result {
if let Some(watcher) = &mut self.watcher {
match watcher.unwatch(Path::new(&msg.0)) {
Ok(_) => info!("Watcher is unset to {}", msg.0),
Err(e) => error!("Could not unset watcher for {}: {}", msg.0, e),
}
}
}
}
impl NotifierActor {
pub fn new(walker: Addr<WalkerActor>) -> Self {
NotifierActor {
watcher: match notify::recommended_watcher(move |res: Result<Event, notify::Error>| {
match res {
Ok(event) => {
event
.process()
.into_iter()
.map(|p| walker.do_send(FileCreated(p)))
.for_each(|_| {});
}
Err(err) => error!("Watcher error: {:?}", err),
}
}) {
Ok(watcher) => Some(watcher),
Err(e) => {
error!("Failed to initialize an FS watcher: {}", e);
None
}
},
}
}
}
trait Process {
fn process(self) -> Vec<PathBuf>;
}
impl Process for Event {
#[cfg(target_os = "linux")]
fn process(self) -> Vec<PathBuf> {
match self.kind {
EventKind::Access(kind) => match kind {
AccessKind::Close(mode) => match mode {
AccessMode::Write => return self.paths.files(),
_ => {}
},
_ => {}
},
_ => {}
}
vec![]
}
#[cfg(not(target_os = "linux"))]
fn process(self) -> Vec<PathBuf> {
match self.kind {
EventKind::Create(_) | EventKind::Modify(_) => return self.paths.files(),
_ => {}
}
vec![]
}
}
trait RetainFiles {
fn files(self) -> Self;
}
impl RetainFiles for Vec<PathBuf> {
fn files(mut self) -> Self {
self.retain(|path| path.is_file());
return self;
}
}