use std::path::{Path, PathBuf}; use notify::{EventKind, RecommendedWatcher, Watcher}; use crate::monitored_files::MonitoredFiles; /// A directory monitor. /// /// This will setup the monitoring of all the sub directories and files /// of the given directory. pub struct DirMonitor { /// The directory to monitor. dir: PathBuf, monitored_files: MonitoredFiles, } impl DirMonitor { /// Create a new directory monitor for the desired directory. /// /// monitor_path: A string representation of directory to monitor. pub fn new(monitor_path: &str) -> Self { DirMonitor { dir: PathBuf::from(monitor_path), monitored_files: MonitoredFiles::new() } } /// Scan the monitored directory and all of its sub directories and monitors /// the files found and their last modification time. pub fn scan(&mut self) { // Start from the directory and add each file to our list, // then recurse through all sub directories and add them to // the list and repeat. match scan_dir(&mut self.monitored_files, &self.dir) { Ok(_) => { } Err(e) => { eprintln!("Directory scanning error: {}", e); } } } /// Print out all the monitored files and their last modified date. /// /// Each file entry will be printed out as: /// ``` /// [Date Time] PATH /// ``` pub fn print_monitored_files(&self) { self.monitored_files.print(&self.dir); } /// pub async fn monitor(&mut self, mut term_receiver: tokio::sync::watch::Receiver) { let mut running: bool = true; //let (notify_sender, notify_receiver) = std::sync::mpsc::channel::>(); let (notify_sender, notify_receiver) = tokio::sync::mpsc::channel::>(100); let mut fs_watcher: RecommendedWatcher = match notify::recommended_watcher(notify_sender) { Ok(watcher) => { watcher } Err(e) => { eprintln!("Unable to create watcher: {}", e); panic!(); } }; fs_watcher.watch(&self.dir, notify::RecursiveMode::Recursive).unwrap(); while running { // Listen for file changes until termination signal is received. match notify_receiver.recv().await { Ok(msg) => { match msg { Ok(event) => { match event.kind { EventKind::Create(_) => { println!("File Created: {:?}", event.paths); } EventKind::Modify(_) => { println!("File Modified: {:?}", event.paths); } EventKind::Remove(_) => { println!("File Removed: {:?}", event.paths); } _ => { } } } Err(e) => { eprintln!("{}", e); } } } Err(e) => { eprintln!("Error receiving notify event: {}", e); } } // Handle listening for the termination message, the boolean value will // be changed to false when we are meant to terminate. match term_receiver.has_changed() { Ok(_) => { running = *term_receiver.borrow_and_update(); } Err(e) => { eprintln!("Unable to receive: {}", e); } } } } } /// Scans a directory, and all of its sub directories, and creates a list of the files /// inside and their last modification time. fn scan_dir(monitored_files: &mut MonitoredFiles, dir: &Path) -> std::io::Result<()> { let mut dir_list: Vec = Vec::new(); for file in std::fs::read_dir(dir)? { let file = file?; let meta = file.metadata()?; // Handle directory and file types. // // Directories will be added to the list and later recursively scanned. // // Files will be added to the list of monitored files and their // last modification time will be stored. // // TODO: Handle symlinks. Symlinks can be either a file or another directory. if meta.is_dir() { dir_list.push(file.path().clone()); } else if meta.is_file() { let last_mod_time: std::time::SystemTime = meta.modified()?; monitored_files.add(&file.path(), last_mod_time); } } // Recursively scan the sub directories. for sub_dir in dir_list { scan_dir(monitored_files, &sub_dir)?; } Ok(()) }