use crate::walker::WalkerActor;
use crate::{dispatcher::IType, Shutdown};
use actix::{Actor, ActorContext, Addr, Handler, Message, Running, SyncContext, System};
#[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 {
itype: IType,
watcher: Option<RecommendedWatcher>,
}
impl Actor for NotifierActor {
type Context = SyncContext<Self>;
fn started(&mut self, _: &mut Self::Context) {
debug!("{} Notifier has started", self.itype);
}
fn stopping(&mut self, _: &mut Self::Context) -> Running {
debug!("{} Notifier is stopping", self.itype);
self.watcher = None;
Running::Stop
}
fn stopped(&mut self, _: &mut Self::Context) {
debug!("{} Notifier has stopped", self.itype);
System::current().stop();
}
}
impl Handler<Shutdown> for NotifierActor {
type Result = ();
fn handle(&mut self, _: Shutdown, ctx: &mut Self::Context) -> Self::Result {
ctx.stop();
}
}
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>, itype: IType) -> Self {
NotifierActor {
itype,
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;
}
}