2025-03-17 14:56:07 -04:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2025-03-19 13:17:26 -04:00
|
|
|
use notify::{EventKind, RecommendedWatcher, Watcher};
|
|
|
|
|
2025-03-17 14:56:07 -04:00
|
|
|
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.
|
2025-03-19 13:17:26 -04:00
|
|
|
dir: PathBuf,
|
2025-03-17 14:56:07 -04:00
|
|
|
|
2025-03-19 13:17:26 -04:00
|
|
|
monitored_files: MonitoredFiles,
|
2025-03-17 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-19 13:17:26 -04:00
|
|
|
/// 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)
|
2025-03-17 14:56:07 -04:00
|
|
|
{
|
|
|
|
// 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) =>
|
|
|
|
{
|
2025-03-19 13:17:26 -04:00
|
|
|
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<bool>)
|
|
|
|
{
|
|
|
|
let mut running: bool = true;
|
|
|
|
|
|
|
|
//let (notify_sender, notify_receiver) = std::sync::mpsc::channel::<Result<notify::Event, notify::Error>>();
|
|
|
|
let (notify_sender, notify_receiver) = tokio::sync::mpsc::channel::<Result<notify::Event, notify::Error>>(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);
|
|
|
|
}
|
2025-03-17 14:56:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-19 13:17:26 -04:00
|
|
|
/// Scans a directory, and all of its sub directories, and creates a list of the files
|
|
|
|
/// inside and their last modification time.
|
2025-03-17 14:56:07 -04:00
|
|
|
fn scan_dir(monitored_files: &mut MonitoredFiles, dir: &Path) -> std::io::Result<()>
|
|
|
|
{
|
|
|
|
let mut dir_list: Vec<PathBuf> = Vec::new();
|
|
|
|
|
|
|
|
for file in std::fs::read_dir(dir)?
|
|
|
|
{
|
|
|
|
let file = file?;
|
2025-03-19 13:17:26 -04:00
|
|
|
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()
|
2025-03-17 14:56:07 -04:00
|
|
|
{
|
|
|
|
dir_list.push(file.path().clone());
|
|
|
|
}
|
2025-03-19 13:17:26 -04:00
|
|
|
else if meta.is_file()
|
|
|
|
{
|
|
|
|
let last_mod_time: std::time::SystemTime = meta.modified()?;
|
|
|
|
|
|
|
|
monitored_files.add(&file.path(), last_mod_time);
|
|
|
|
}
|
2025-03-17 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
2025-03-19 13:17:26 -04:00
|
|
|
// Recursively scan the sub directories.
|
2025-03-17 14:56:07 -04:00
|
|
|
for sub_dir in dir_list
|
|
|
|
{
|
|
|
|
scan_dir(monitored_files, &sub_dir)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|