bubbles 4.1.0

Bubble integration server for powder diffraction
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;
    }
}