From a56c6df3f1ad8943185941ca733a4d91069994c1 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 28 Jul 2024 15:37:58 +0100 Subject: [PATCH] feat: support macOS Closes kemitix/git-next#108 --- Cargo.toml | 2 +- crates/cli/Cargo.toml | 2 +- crates/cli/src/file_watcher.rs | 90 +++++++++++++----------------- crates/cli/src/server/actor/mod.rs | 1 - crates/cli/src/server/mod.rs | 15 ++--- 5 files changed, 45 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9182344c..c1dece52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,7 +91,7 @@ thiserror = "1.0" pike = "0.1" # file watcher -inotify = "0.10" +notify = "6.1" # Actors actix = "0.13" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 6d57d42a..a42d39fc 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -65,7 +65,7 @@ bytes = { workspace = true } warp = { workspace = true } # file watcher (linux) -inotify = { workspace = true } +notify = { workspace = true } [dev-dependencies] # Testing diff --git a/crates/cli/src/file_watcher.rs b/crates/cli/src/file_watcher.rs index 1235c1e7..ca0156a3 100644 --- a/crates/cli/src/file_watcher.rs +++ b/crates/cli/src/file_watcher.rs @@ -2,15 +2,13 @@ use actix::prelude::*; use actix::Recipient; -use inotify::{EventMask, Inotify, WatchMask}; +use notify::event::ModifyKind; +use notify::Watcher; +use tracing::error; +use tracing::info; -use std::{path::PathBuf, time::Duration}; - -const CHECK_INTERVAL: Duration = Duration::from_secs(1); - -#[derive(Debug, Clone, Message)] -#[rtype(result = "()")] -pub struct WatchFile; +use std::path::PathBuf; +use std::time::Duration; #[derive(Debug, Message)] #[rtype(result = "()")] @@ -21,50 +19,40 @@ pub enum Error { #[error("io")] Io(#[from] std::io::Error), } +pub async fn watch_file(path: PathBuf, recipient: Recipient) { + let (tx, rx) = std::sync::mpsc::channel(); -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); - }; + #[allow(clippy::expect_used)] + let mut handler = notify::recommended_watcher(tx).expect("file watcher"); + #[allow(clippy::expect_used)] + handler + .watch(&path, notify::RecursiveMode::NonRecursive) + .expect("watch file"); + info!("Watching: {:?}", path); + async move { + loop { + for result in rx.try_iter() { + match result { + Ok(event) => match event.kind { + notify::EventKind::Modify(ModifyKind::Data(_)) => { + tracing::info!("File modified"); + recipient.do_send(FileUpdated); + break; + } + notify::EventKind::Modify(_) + | notify::EventKind::Create(_) + | notify::EventKind::Remove(_) + | notify::EventKind::Any + | notify::EventKind::Access(_) + | notify::EventKind::Other => { /* do nothing */ } + }, + Err(err) => { + error!(?err, "Watching file: {path:?}"); + } + } + } + actix_rt::time::sleep(Duration::from_millis(1000)).await; } } + .await; } diff --git a/crates/cli/src/server/actor/mod.rs b/crates/cli/src/server/actor/mod.rs index f40e8fb4..dd4e717f 100644 --- a/crates/cli/src/server/actor/mod.rs +++ b/crates/cli/src/server/actor/mod.rs @@ -243,6 +243,5 @@ impl ServerActor { } #[cfg(not(test))] _ctx.address().do_send(msg); - tracing::info!("sent"); } } diff --git a/crates/cli/src/server/mod.rs b/crates/cli/src/server/mod.rs index d5136a16..e6cf458f 100644 --- a/crates/cli/src/server/mod.rs +++ b/crates/cli/src/server/mod.rs @@ -6,12 +6,12 @@ mod tests; use actix::prelude::*; -use crate::file_watcher::{FileUpdated, FileWatcher}; +use crate::file_watcher::{watch_file, FileUpdated}; use actor::ServerActor; use git_next_core::git::RepositoryFactory; use kxio::{fs::FileSystem, network::Network}; -use tracing::{error, info, level_filters::LevelFilter}; +use tracing::{info, level_filters::LevelFilter}; use std::path::PathBuf; @@ -45,20 +45,13 @@ pub fn start( ) { init_logging(); - info!("Starting Server..."); let execution = async move { + info!("Starting Server..."); let server = ServerActor::new(fs.clone(), net.clone(), repo, sleep_duration).start(); server.do_send(FileUpdated); info!("Starting File Watcher..."); - let fw = match FileWatcher::new("git-next-server.toml".into(), server.clone().recipient()) { - Ok(fw) => fw, - Err(err) => { - error!(?err, "Failed to start file watcher"); - return; - } - }; - fw.start(); + watch_file("git-next-server.toml".into(), server.clone().recipient()).await; info!("Server running - Press Ctrl-C to stop..."); let _ = actix_rt::signal::ctrl_c().await;