2025-03-17 14:56:07 -04:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
use notify::{RecommendedWatcher, Watcher};
|
2025-03-19 13:17:26 -04:00
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
use crate::event::Event;
|
2025-03-17 14:56:07 -04:00
|
|
|
use crate::monitored_files::MonitoredFiles;
|
2025-03-20 02:18:30 -04:00
|
|
|
use crate::notify_sender::NotifySender;
|
|
|
|
use crate::util::strip_path_prefix;
|
2025-03-17 14:56:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 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-20 02:18:30 -04:00
|
|
|
/// The files that are being monitored within the directory.
|
2025-03-19 13:17:26 -04:00
|
|
|
monitored_files: MonitoredFiles,
|
2025-03-17 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
|
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.
|
2025-03-20 02:18:30 -04:00
|
|
|
match scan_dir(&self.dir, &mut self.monitored_files, &self.dir)
|
2025-03-17 14:56:07 -04:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
println!("{}", self.monitored_files);
|
2025-03-19 13:17:26 -04:00
|
|
|
}
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
/// Start monitoring the directory asyncronously and process changes within the directory
|
|
|
|
/// until a termination message is received.
|
2025-03-19 13:17:26 -04:00
|
|
|
pub async fn monitor(&mut self, mut term_receiver: tokio::sync::watch::Receiver<bool>)
|
|
|
|
{
|
|
|
|
let mut running: bool = true;
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
// Setup the notify crate to watch the INBOX directory.
|
|
|
|
let tokio_runtime = tokio::runtime::Handle::current();
|
|
|
|
let (notify_sender, mut notify_receiver) = tokio::sync::mpsc::channel::<Event>(10);
|
|
|
|
let wrapped_sender = NotifySender::new(tokio_runtime, notify_sender);
|
|
|
|
let mut fs_watcher: RecommendedWatcher =
|
|
|
|
match notify::recommended_watcher(wrapped_sender)
|
2025-03-19 13:17:26 -04:00
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
Ok(watcher) => { watcher }
|
2025-03-19 13:17:26 -04:00
|
|
|
Err(e) =>
|
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
// Just panic because we cannot watch the directories so the program
|
|
|
|
// would be useless and this saves from having to press CTRL-C.
|
|
|
|
eprintln!("Unable to create watcher: {}", e);
|
|
|
|
panic!();
|
2025-03-19 13:17:26 -04:00
|
|
|
}
|
2025-03-20 02:18:30 -04:00
|
|
|
};
|
2025-03-19 13:17:26 -04:00
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
if let Err(e) = fs_watcher.watch(&self.dir, notify::RecursiveMode::Recursive)
|
|
|
|
{
|
|
|
|
// Just panic because we cannot watch the directories so the program
|
|
|
|
// would be useless and this saves from having to press CTRL-C.
|
|
|
|
eprintln!("Error trying to watch the directory: {}", e);
|
|
|
|
panic!();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Loop until termination processing events from the notify watcher.
|
|
|
|
while running
|
|
|
|
{
|
|
|
|
// We are listening for messages from either the notify receiver or
|
|
|
|
// the termination receiver. When ever one of them comes across we
|
|
|
|
// will process it.
|
|
|
|
tokio::select!
|
2025-03-19 13:17:26 -04:00
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
// Handle listening for the notify watcher events.
|
|
|
|
// These are the changes that we care about from the file system.
|
|
|
|
Some(mut event) = notify_receiver.recv() =>
|
2025-03-19 13:17:26 -04:00
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
event.make_paths_relative(&self.dir);
|
|
|
|
println!("{}", event);
|
|
|
|
self.monitored_files.process_event(event);
|
2025-03-19 13:17:26 -04:00
|
|
|
}
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
|
|
|
|
// Handle listening for the termination message, the boolean value will
|
|
|
|
// be changed to false when we are meant to terminate.
|
|
|
|
_ = term_receiver.changed() =>
|
2025-03-19 13:17:26 -04:00
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
running = *term_receiver.borrow_and_update();
|
2025-03-19 13:17:26 -04:00
|
|
|
}
|
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-20 02:18:30 -04:00
|
|
|
fn scan_dir(base_dir: &Path, monitored_files: &mut MonitoredFiles, dir: &Path) -> std::io::Result<()>
|
2025-03-17 14:56:07 -04:00
|
|
|
{
|
|
|
|
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()?;
|
|
|
|
|
2025-03-20 02:18:30 -04:00
|
|
|
monitored_files.add(&strip_path_prefix(&file.path(), base_dir), last_mod_time);
|
2025-03-19 13:17:26 -04:00
|
|
|
}
|
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
|
|
|
|
{
|
2025-03-20 02:18:30 -04:00
|
|
|
scan_dir(base_dir, monitored_files, &sub_dir)?;
|
2025-03-17 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|