use crate::options::structs::{CustomError, Files}; use crate::utils::prcs::{is_active, is_frozen}; use inotify::{EventMask, Inotify, WatchMask}; use std::borrow::BorrowMut; use std::path::Path; use std::sync::Arc; use tokio::sync::mpsc; use tokio::time::Duration; pub async fn create_watcher(filename: &str, path: &str) -> Result { let src = format!("{}{}", path, filename); let inotify: Inotify = Inotify::init()?; inotify.watches().add(&src, WatchMask::ALL_EVENTS)?; Ok(inotify) } pub async fn file_handler( name: &str, files: &[Files], tx: Arc>, watchers: Arc>>, ) -> Result<(), CustomError> { for (i, file) in files.iter().enumerate() { // let src = format!("{}{}", file.src, file.filename); if check_file(&file.filename, &file.src).await.is_err() { if !is_active(name).await || is_frozen(name).await { return Err(CustomError::Fatal); } match file.triggers.on_delete.as_str() { "stay" => { tx.send(9).await.unwrap(); continue; } "stop" => { if is_active(name).await { tx.send(1).await.unwrap(); } return Err(CustomError::Fatal); } "hold" => { if is_active(name).await { tx.send(2).await.unwrap(); return Err(CustomError::Fatal); } } _ => { tokio::time::sleep(Duration::from_millis(50)).await; tx.send(101).await.unwrap(); return Err(CustomError::Fatal); } } } else if is_active(name).await && !is_frozen(name).await { let watchers = watchers.clone(); // println!("mutex: {:?}", watchers); let mut buffer = [0; 128]; let mut mutex_guard = watchers.lock().await; if let Some(notify) = mutex_guard.get_mut(i) { let events = notify.read_events(&mut buffer); // println!("{:?}", events); if events.is_ok() { let events: Vec = events .unwrap() .map(|mask| mask.mask) .filter(|mask| { *mask == EventMask::MODIFY || *mask == EventMask::DELETE_SELF }) .collect(); for event in events { if let EventMask::DELETE_SELF = event { // ! warning (DELETE_SELF event) ! // println!("! warning (DELETE_SELF event) !"); // * watcher recreation after dealing with file recreation mechanism in text editors let mutex = notify.borrow_mut(); *mutex = create_watcher(&file.filename, &file.src).await.unwrap(); } match file.triggers.on_change.as_str() { "stop" => { let _ = tx.send(7).await; } "restart" => { let _ = tx.send(8).await; } "stay" => { let _ = tx.send(9).await; } _ => { let _ = tx.send(101).await; } } } } } } } tokio::task::yield_now().await; Ok(()) } pub async fn check_file(filename: &str, path: &str) -> Result<(), CustomError> { let arc_name = Arc::new(filename.to_string()); let arc_path = Arc::new(path.to_string()); tokio::task::spawn_blocking(move || { let file_concat = format!("{}{}", arc_path, arc_name); let path = Path::new(&file_concat); if path.exists() { Ok(()) } else { Err(CustomError::Fatal) } }) .await .unwrap_or_else(|_| { panic!("Corrupted while file check process"); }) } #[cfg(test)] mod files_unittests { use super::*; #[tokio::test] async fn try_to_create_watcher() { let res = create_watcher("dep-file", "/home/user/monitor/runner-rs/tests/examples/").await; assert!(res.is_ok()); } #[tokio::test] async fn try_to_create_invalid_watcher() { let res = create_watcher("invalid-file", "/path/to/the/hell").await; assert!(res.is_err()); } #[tokio::test] async fn check_existing_file() { let res = check_file("dep-file", "/home/user/monitor/runner-rs/tests/examples/").await; assert!(res.is_ok()); } #[tokio::test] async fn check_non_existing_file() { let res = check_file("invalid-file", "/path/to/the/hell").await; assert!(res.is_err()); } }