file_monitor/src/dir_monitor.rs

174 lines
5.4 KiB
Rust
Raw Normal View History

use std::path::{Path, PathBuf};
2025-03-20 02:18:30 -04:00
use notify::{RecommendedWatcher, Watcher};
2025-03-20 02:18:30 -04:00
use crate::event::Event;
use crate::monitored_files::MonitoredFiles;
2025-03-20 02:18:30 -04:00
use crate::notify_sender::NotifySender;
use crate::util::strip_path_prefix;
/// 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,
2025-03-20 02:18:30 -04:00
/// The files that are being monitored within the directory.
monitored_files: MonitoredFiles,
}
2025-03-20 02:18:30 -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()
}
}
/// 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.
2025-03-20 02:18:30 -04:00
match scan_dir(&self.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)
{
2025-03-20 02:18:30 -04:00
println!("{}", self.monitored_files);
}
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.
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-20 02:18:30 -04:00
Ok(watcher) => { watcher }
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-20 02:18:30 -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-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-20 02:18:30 -04:00
event.make_paths_relative(&self.dir);
println!("{}", event);
self.monitored_files.process_event(event);
}
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-20 02:18:30 -04:00
running = *term_receiver.borrow_and_update();
}
}
}
}
}
/// 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<()>
{
let mut dir_list: Vec<PathBuf> = 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()?;
2025-03-20 02:18:30 -04:00
monitored_files.add(&strip_path_prefix(&file.path(), base_dir), last_mod_time);
}
}
// Recursively scan the sub directories.
for sub_dir in dir_list
{
2025-03-20 02:18:30 -04:00
scan_dir(base_dir, monitored_files, &sub_dir)?;
}
Ok(())
}