use actix::prelude::*; use actix::Recipient; use inotify::{EventMask, Inotify, WatchMask}; use std::{path::PathBuf, time::Duration}; const CHECK_INTERVAL: Duration = Duration::from_secs(1); #[derive(Debug, Clone, Message)] #[rtype(result = "()")] pub struct WatchFile; #[derive(Debug, Message)] #[rtype(result = "()")] pub struct FileUpdated; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("io")] Io(#[from] std::io::Error), } pub struct FileWatcher { inotify: Inotify, recipient: Recipient, run_interval: Option, } impl FileWatcher { pub fn new(path: PathBuf, recipient: Recipient) -> Result { let inotify = Inotify::init()?; inotify.watches().add( path, WatchMask::MODIFY | WatchMask::CREATE | WatchMask::DELETE | WatchMask::ATTRIB, )?; Ok(Self { inotify, recipient, run_interval: None, }) } } impl Actor for FileWatcher { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { tracing::info!("Starting file watcher actor"); self.run_interval .replace(ctx.run_interval(CHECK_INTERVAL, |_act, ctx| { ctx.notify(WatchFile); })); } } impl Handler for FileWatcher { type Result = (); fn handle(&mut self, _msg: WatchFile, _ctx: &mut Self::Context) -> Self::Result { let mut buffer = [0u8; 4096]; if let Ok(mut events) = self.inotify.read_events(&mut buffer) { if events.any(|event| { event.mask.contains(EventMask::MODIFY) || event.mask.contains(EventMask::ATTRIB) }) { tracing::info!("File modified"); self.recipient.do_send(FileUpdated); }; } } }