file_monitor/src/dir_monitor.rs

184 lines
5.5 KiB
Rust
Raw Normal View History

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<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);
}
}
}
}
}
/// 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<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()?;
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(())
}