From 3a0684c33e20a6e4263c8a73ee43fa5133b3fd87 Mon Sep 17 00:00:00 2001 From: Myrddin Dundragon Date: Tue, 25 Mar 2025 22:23:34 -0400 Subject: [PATCH] Ran 'cargo fmt' to adjust the files. Just trying to get closer to my preferred coding style. --- src/dir_monitor.rs | 258 ++++++++++++++--------------- src/event.rs | 164 ++++++++++--------- src/main.rs | 162 +++++++++---------- src/monitored_files.rs | 143 +++++++++-------- src/notify_sender.rs | 357 +++++++++++++++++++---------------------- src/util.rs | 48 +++--- 6 files changed, 559 insertions(+), 573 deletions(-) diff --git a/src/dir_monitor.rs b/src/dir_monitor.rs index d98f9a9..7497d75 100644 --- a/src/dir_monitor.rs +++ b/src/dir_monitor.rs @@ -15,159 +15,161 @@ use crate::util::strip_path_prefix; /// of the given directory. pub struct DirMonitor { - /// The directory to monitor. - dir: PathBuf, + /// The directory to monitor. + dir: PathBuf, - /// The files that are being monitored within the directory. - monitored_files: MonitoredFiles, + /// The files that are being monitored within the directory. + 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() - } - } + /// 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(&self.dir, &mut self.monitored_files, &self.dir) - { - Ok(_) => - { - } + /// 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(&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) + { + println!("{}", self.monitored_files); + } + + /// 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) + { + let mut running: bool = true; + + // 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::(10); + let wrapped_sender = NotifySender::new(tokio_runtime, notify_sender); + let mut fs_watcher: RecommendedWatcher = + match notify::recommended_watcher(wrapped_sender) + { + Ok(watcher) => watcher, Err(e) => { - eprintln!("Directory scanning error: {}", e); + // 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!(); } - } - } + }; - /// 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) - { - println!("{}", self.monitored_files); - } - - /// 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) - { - let mut running: bool = true; - - // 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::(10); - let wrapped_sender = NotifySender::new(tokio_runtime, notify_sender); - let mut fs_watcher: RecommendedWatcher = - match notify::recommended_watcher(wrapped_sender) - { - Ok(watcher) => { watcher } - Err(e) => - { - // 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!(); - } - }; - - 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!(); - } + 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! - { - // 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() => - { - event.make_paths_relative(&self.dir); - println!("{}", event); - self.monitored_files.process_event(event); - } + // 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! { + // 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() => + { + event.make_paths_relative(&self.dir); + println!("{}", event); + self.monitored_files.process_event(event); + } - // Handle listening for the termination message, the boolean value will - // be changed to false when we are meant to terminate. - _ = term_receiver.changed() => - { - running = *term_receiver.borrow_and_update(); - } - } - } - } + // Handle listening for the termination message, the boolean value will + // be changed to false when we are meant to terminate. + _ = term_receiver.changed() => + { + 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. -fn scan_dir(base_dir: &Path, monitored_files: &mut MonitoredFiles, dir: &Path) -> std::io::Result<()> +/// 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(base_dir: &Path, monitored_files: &mut MonitoredFiles, dir: &Path) + -> std::io::Result<()> { - let mut dir_list: Vec = Vec::new(); + let mut dir_list: Vec = Vec::new(); - for file in std::fs::read_dir(dir)? - { - let file = file?; - let meta = file.metadata()?; + 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()?; + // 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(&strip_path_prefix(&file.path(), base_dir), last_mod_time); - } - } + monitored_files.add(&strip_path_prefix(&file.path(), base_dir), + last_mod_time); + } + } - // Recursively scan the sub directories. - for sub_dir in dir_list - { - scan_dir(base_dir, monitored_files, &sub_dir)?; - } + // Recursively scan the sub directories. + for sub_dir in dir_list + { + scan_dir(base_dir, monitored_files, &sub_dir)?; + } - Ok(()) + Ok(()) } diff --git a/src/event.rs b/src/event.rs index b753795..a356ef4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -9,102 +9,106 @@ use crate::util::strip_path_prefix; /// These will be sent as messages in a channel. pub enum Event { - /// A file has been created. - New - { - /// The path to the file. - path: std::path::PathBuf, + /// A file has been created. + New + { + /// The path to the file. + path: std::path::PathBuf, - /// The modification time of the file. - mod_time: SystemTime - }, + /// The modification time of the file. + mod_time: SystemTime + }, - /// A file has been modified. - Modify - { - /// The path to the file. - path: std::path::PathBuf, + /// A file has been modified. + Modify + { + /// The path to the file. + path: std::path::PathBuf, - /// The modification time of the file. - mod_time: SystemTime - }, + /// The modification time of the file. + mod_time: SystemTime + }, - /// A file has been renamed, and it stayed in a monitored directory. - Rename - { - /// The path of the file that was renamed. - from: std::path::PathBuf, + /// A file has been renamed, and it stayed in a monitored directory. + Rename + { + /// The path of the file that was renamed. + from: std::path::PathBuf, - /// The path that the file was renamed to. - to: std::path::PathBuf, + /// The path that the file was renamed to. + to: std::path::PathBuf, - /// The modification time of the file. - mod_time: SystemTime - }, + /// The modification time of the file. + mod_time: SystemTime + }, - /// A file has been removed. - Delete - { - /// The path of the removed file. - path: std::path::PathBuf - } + /// A file has been removed. + Delete + { + /// The path of the removed file. + path: std::path::PathBuf + } } impl Event { - /// Take all the paths in the Event and makes them relative - /// to the base_dir directory. - pub fn make_paths_relative(&mut self, base_dir: &std::path::Path) - { - match self - { - Event::New { path, mod_time: _ } => - { - *path = strip_path_prefix(&path, base_dir); - } - Event::Modify { path, mod_time: _ } => - { - *path = strip_path_prefix(&path, base_dir); - } - Event::Rename { from, to, mod_time: _ } => - { - *from = strip_path_prefix(&from, base_dir); - *to = strip_path_prefix(&to, base_dir); - } - Event::Delete { path } => - { - *path = strip_path_prefix(&path, base_dir); - } - } - } + /// Take all the paths in the Event and makes them relative + /// to the base_dir directory. + pub fn make_paths_relative(&mut self, base_dir: &std::path::Path) + { + match self + { + Event::New { path, mod_time: _ } => + { + *path = strip_path_prefix(&path, base_dir); + } + Event::Modify { path, mod_time: _ } => + { + *path = strip_path_prefix(&path, base_dir); + } + Event::Rename { from, + to, + mod_time: _ } => + { + *from = strip_path_prefix(&from, base_dir); + *to = strip_path_prefix(&to, base_dir); + } + Event::Delete { path } => + { + *path = strip_path_prefix(&path, base_dir); + } + } + } } impl std::fmt::Display for Event { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - match self - { - Event::New { path, mod_time: _ } => - { - write!(f, "[NEW] {}", path.display()) - } - Event::Modify { path, mod_time: _ } => - { - write!(f, "[MOD] {}", path.display()) - } - Event::Rename { from, to, mod_time: _ } => - { - writeln!(f, "[DEL] {}", from.display())?; - write!(f, "[NEW] {}", to.display()) - } - Event::Delete { path } => - { - write!(f, "[DEL] {}", path.display()) - } - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self + { + Event::New { path, mod_time: _ } => + { + write!(f, "[NEW] {}", path.display()) + } + Event::Modify { path, mod_time: _ } => + { + write!(f, "[MOD] {}", path.display()) + } + Event::Rename { from, + to, + mod_time: _ } => + { + writeln!(f, "[DEL] {}", from.display())?; + write!(f, "[NEW] {}", to.display()) + } + Event::Delete { path } => + { + write!(f, "[DEL] {}", path.display()) + } + } + } } diff --git a/src/main.rs b/src/main.rs index f47333b..3d219ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ mod util; use clap::Parser; - use tokio::sync::watch; use crate::dir_monitor::DirMonitor; @@ -27,9 +26,9 @@ use crate::dir_monitor::DirMonitor; /// files. struct Options { - #[arg(short = 'd', default_value = "~/inbox")] - /// The path for the inbox to monitor. - inbox_dir: String + #[arg(short = 'd', default_value = "~/inbox")] + /// The path for the inbox to monitor. + inbox_dir: String } @@ -37,28 +36,25 @@ struct Options /// Creates the given directory, and its parents, if it does not exist. fn create_directory(dir: &str) -> Option { - let mut inbox_dir = String::from(dir); - if inbox_dir.starts_with("~") - { - if let Some(path) = home::home_dir() - { - inbox_dir = inbox_dir.replace("~", &path.display().to_string()); - } - } + let mut inbox_dir = String::from(dir); + if inbox_dir.starts_with("~") + { + if let Some(path) = home::home_dir() + { + inbox_dir = inbox_dir.replace("~", &path.display().to_string()); + } + } - match std::fs::create_dir_all(inbox_dir.clone()) - { - Ok(_) => - { - Some(inbox_dir) - } + match std::fs::create_dir_all(inbox_dir.clone()) + { + Ok(_) => Some(inbox_dir), - Err(e) => - { - eprintln!("Creating directory error: {}", e); - None - } - } + Err(e) => + { + eprintln!("Creating directory error: {}", e); + None + } + } } /// Asynchronously listen for CTRL-C to be pressed. @@ -67,13 +63,13 @@ fn create_directory(dir: &str) -> Option /// when it detects the termination signal. async fn listen_for_termination(sender: watch::Sender) { - tokio::signal::ctrl_c().await.unwrap(); + tokio::signal::ctrl_c().await.unwrap(); - // CTRL-C was pressed so send a message to everyone that is - // watching that the program is to be terminated. Then close the - // sender. - sender.send_replace(false); - sender.closed().await; + // CTRL-C was pressed so send a message to everyone that is + // watching that the program is to be terminated. Then close the + // sender. + sender.send_replace(false); + sender.closed().await; } @@ -81,63 +77,65 @@ async fn listen_for_termination(sender: watch::Sender) #[tokio::main] async fn main() { - let options: Options = Options::parse(); + let options: Options = Options::parse(); - if let Some(inbox_dir) = create_directory(&options.inbox_dir) - { - // This is our keep running channel. False will be sent - // when the application is meant to be closed. - let (sender, receiver) = watch::channel(true); + if let Some(inbox_dir) = create_directory(&options.inbox_dir) + { + // This is our keep running channel. False will be sent + // when the application is meant to be closed. + let (sender, receiver) = watch::channel(true); - let mut directory_monitor: DirMonitor = DirMonitor::new(&inbox_dir); + let mut directory_monitor: DirMonitor = DirMonitor::new(&inbox_dir); - // Program workflow step 1. - // Async is not needed here. This can be done syncronously because - // file systems are not async and the spawn_blocking blocking overhead - // isn't needed. - directory_monitor.scan(); - directory_monitor.print_monitored_files(); + // Program workflow step 1. + // Async is not needed here. This can be done syncronously because + // file systems are not async and the spawn_blocking blocking overhead + // isn't needed. + directory_monitor.scan(); + directory_monitor.print_monitored_files(); - // Run our directory monitor. - let mon_task = tokio::spawn(async move - { - // Program workflow step 2 (terminated by step 3). - // This can be done async because we are waiting on - // events to be loaded from the notify library and - // we want this to run continously and not block the - // program. This could also be done on a seperate thread, - // preferably a green thread, but a fiber is sufficient - // and less resource intensive. - directory_monitor.monitor(receiver).await; + // Run our directory monitor. + let mon_task = tokio::spawn(async move { + // Program workflow step 2 (terminated by step 3). + // This can be done async because we are waiting on + // events to be loaded from the notify library and + // we want this to run continously and not block the + // program. This could also be done on a seperate thread, + // preferably a green thread, but a fiber is sufficient + // and less resource intensive. + directory_monitor.monitor(receiver).await; - // Program workflow step 4. - // Async is not needed here. This can be done - // syncronously because it is the final/clean up - // step of the program. - // - // This is done here because we have moved the directory - // monitor inside this task. - directory_monitor.print_monitored_files(); - }); + // Program workflow step 4. + // Async is not needed here. This can be done + // syncronously because it is the final/clean up + // step of the program. + // + // This is done here because we have moved the directory + // monitor inside this task. + directory_monitor.print_monitored_files(); + }); - // Run until Ctrl-C is pressed. - // Once it is pressed it will send a message to stop. - let _term_task = tokio::spawn(async move - { - // Program workflow step 3. - // Async with message passing so it can run and listen - // for the termination signal. It uses a watch channel - // because any task that will need to know when the - // program is to end can then catch the change. - listen_for_termination(sender).await; - }); + // Run until Ctrl-C is pressed. + // Once it is pressed it will send a message to stop. + let _term_task = tokio::spawn(async move { + // Program workflow step 3. + // Async with message passing so it can run and listen + // for the termination signal. It uses a watch channel + // because any task that will need to know when the + // program is to end can then catch the change. + listen_for_termination(sender).await; + }); - // Only need to wait for the monitor task to finish because it will only - // finish once the termination task has finished. - match mon_task.await - { - Ok(_) => {} - Err(e) => { eprintln!("Error during monitoring task: {}", e); } - } - } + // Only need to wait for the monitor task to finish because it will only + // finish once the termination task has finished. + match mon_task.await + { + Ok(_) => + {} + Err(e) => + { + eprintln!("Error during monitoring task: {}", e); + } + } + } } diff --git a/src/monitored_files.rs b/src/monitored_files.rs index 5883f58..fc343f5 100644 --- a/src/monitored_files.rs +++ b/src/monitored_files.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; use std::time::SystemTime; use chrono::{DateTime, Local}; @@ -11,93 +11,96 @@ use crate::event::Event; /// The files currently being monitored and their last modification time. pub struct MonitoredFiles { - /// A mapping of a Path to the last known modification time. - files: HashMap + /// A mapping of a Path to the last known modification time. + files: HashMap } impl MonitoredFiles { - /// Create a new empty set of monitored files. - pub fn new() -> Self - { - MonitoredFiles - { - files: HashMap::new() - } - } + /// Create a new empty set of monitored files. + pub fn new() -> Self + { + MonitoredFiles { files: HashMap::new() } + } - /// Process an event and change the monitored files. - pub fn process_event(&mut self, event: Event) - { - match event - { - Event::New { path, mod_time } => - { - self.add(&path, mod_time); - } + /// Process an event and change the monitored files. + pub fn process_event(&mut self, event: Event) + { + match event + { + Event::New { path, mod_time } => + { + self.add(&path, mod_time); + } - Event::Modify { path, mod_time} => - { - self.modify(&path, mod_time); - } + Event::Modify { path, mod_time } => + { + self.modify(&path, mod_time); + } - Event::Rename { from, to, mod_time } => - { - self.remove(&from); - self.add(&to, mod_time); - } + Event::Rename { from, to, mod_time } => + { + self.remove(&from); + self.add(&to, mod_time); + } - Event::Delete { path } => - { - self.remove(&path); - } - } - } + Event::Delete { path } => + { + self.remove(&path); + } + } + } - /// Add a file that is to be monitored.. - pub fn add(&mut self, path: &Path, mod_time: SystemTime) - { - self.files.insert(PathBuf::from(path), mod_time); - } + /// Add a file that is to be monitored.. + pub fn add(&mut self, path: &Path, mod_time: SystemTime) + { + self.files.insert(PathBuf::from(path), mod_time); + } - /// Modify the stored modifcation time of a monitored file. - pub fn modify(&mut self, path: &Path, time: SystemTime) - { - if let Some(entry) = self.files.get_mut(path) - { - *entry = time; - } - } + /// Modify the stored modifcation time of a monitored file. + pub fn modify(&mut self, path: &Path, time: SystemTime) + { + if let Some(entry) = self.files.get_mut(path) + { + *entry = time; + } + } - /// Remove a file from being monitored. - pub fn remove(&mut self, path: &Path) - { - self.files.remove(path); - } + /// Remove a file from being monitored. + pub fn remove(&mut self, path: &Path) + { + self.files.remove(path); + } } impl std::fmt::Display for MonitoredFiles { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - let mut it = self.files.iter().peekable(); - while let Some((path, mod_time)) = it.next() - { - let date_time: DateTime = DateTime::from(*mod_time); + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + let mut it = self.files.iter().peekable(); + while let Some((path, mod_time)) = it.next() + { + let date_time: DateTime = DateTime::from(*mod_time); - if !it.peek().is_none() - { - writeln!(f, "[{}] {}", date_time.format("%m/%d/%Y %H:%M"), path.display())?; - } - else - { - write!(f, "[{}] {}", date_time.format("%m/%d/%Y %H:%M"), path.display())?; - } - } + if !it.peek().is_none() + { + writeln!(f, + "[{}] {}", + date_time.format("%m/%d/%Y %H:%M"), + path.display())?; + } + else + { + write!(f, + "[{}] {}", + date_time.format("%m/%d/%Y %H:%M"), + path.display())?; + } + } - Ok(()) - } + Ok(()) + } } diff --git a/src/notify_sender.rs b/src/notify_sender.rs index 4dcad0c..f85b1bb 100644 --- a/src/notify_sender.rs +++ b/src/notify_sender.rs @@ -10,236 +10,215 @@ use crate::util::get_file_mod_time; /// EventHandler. pub struct NotifySender { - /// The tokio runtime to spawn event handling tasks on. - tokio_runtime: tokio::runtime::Handle, + /// The tokio runtime to spawn event handling tasks on. + tokio_runtime: tokio::runtime::Handle, - /// The channel to send event messages on. - sender: tokio::sync::mpsc::Sender + /// The channel to send event messages on. + sender: tokio::sync::mpsc::Sender } impl NotifySender { - /// Create a new type that wraps the tokio mpsc Sender. - pub fn new(tokio_runtime: tokio::runtime::Handle, - sender: tokio::sync::mpsc::Sender) - -> Self - { - NotifySender - { - tokio_runtime, - sender - } - } + /// Create a new type that wraps the tokio mpsc Sender. + pub fn new(tokio_runtime: tokio::runtime::Handle, + sender: tokio::sync::mpsc::Sender) + -> Self + { + NotifySender { tokio_runtime, + sender } + } } impl notify::EventHandler for NotifySender { - fn handle_event(&mut self, event: notify::Result) - { - match event - { - Ok(notify_event) => + fn handle_event(&mut self, event: notify::Result) + { + match event + { + Ok(notify_event) => + { + // Process the notify event and turn it into an event + // our crate is interested in, one of our Events. If + // we get a valid one then create a tokio task that will + // send the event out as a message. + if let Some(monitor_event) = process_notify_event(notify_event) { - // Process the notify event and turn it into an event - // our crate is interested in, one of our Events. If - // we get a valid one then create a tokio task that will - // send the event out as a message. - if let Some(monitor_event) = process_notify_event(notify_event) - { - let notify_sender = self.sender.clone(); + let notify_sender = self.sender.clone(); - self.tokio_runtime.spawn(async move - { - if let Err(e) = notify_sender.send(monitor_event).await - { - eprintln!("Notify EventHandler: {}", e); - } - }); - } + self.tokio_runtime.spawn(async move { + if let Err(e) = + notify_sender.send(monitor_event).await + { + eprintln!("Notify EventHandler: {}", e); + } + }); } + } - Err(e) => - { - eprintln!("Notify crate error: {}", e); - } - } - } + Err(e) => + { + eprintln!("Notify crate error: {}", e); + } + } + } } /// Processes the notify crate events into events that our program cares about. fn process_notify_event(event: notify::Event) -> Option { - match event.kind - { - notify::EventKind::Create(kind) => - { - process_create_event(kind, event.paths) - } + match event.kind + { + notify::EventKind::Create(kind) => + { + process_create_event(kind, event.paths) + } - notify::EventKind::Modify(kind) => - { - process_modify_event(kind, event.paths) - } + notify::EventKind::Modify(kind) => + { + process_modify_event(kind, event.paths) + } - notify::EventKind::Remove(kind) => - { - process_remove_event(kind, event.paths) - } + notify::EventKind::Remove(kind) => + { + process_remove_event(kind, event.paths) + } - _ => { None } - } + _ => None + } } /// Process the Create event into a New event. -fn process_create_event(event: notify::event::CreateKind, - paths: Vec) - -> Option +fn process_create_event(event: notify::event::CreateKind, paths: Vec) + -> Option { - if event == notify::event::CreateKind::File - { - let mod_time: std::time::SystemTime = - match get_file_mod_time(&paths[0]) - { - Ok(time) => { time } + if event == notify::event::CreateKind::File + { + let mod_time: std::time::SystemTime = match get_file_mod_time(&paths[0]) + { + Ok(time) => time, - Err(e) => - { - eprintln!("Unable to open file to retrieve modification time: {}", - e); - std::time::SystemTime::now() - } - }; + Err(e) => + { + eprintln!("Unable to open file to retrieve modification time: {}", + e); + std::time::SystemTime::now() + } + }; - return Some(crate::event::Event::New - { - path: paths[0].clone(), - mod_time: mod_time - }); - } + return Some(crate::event::Event::New { path: paths[0].clone(), + mod_time: mod_time }); + } - None + None } /// Process the modify event into either a Modify event or a Rename event. -fn process_modify_event(event: notify::event::ModifyKind, - paths: Vec) - -> Option +fn process_modify_event(event: notify::event::ModifyKind, paths: Vec) + -> Option { - match event - { - // This is just handling the events. On my system they came across as Any for - // the MetadataKind, but the documentation seemed to hint that modification - // times should be on write events. So for the sake of being done without testing - // this on different platforms the Any modification event will trigger a time lookup. - notify::event::ModifyKind::Any | notify::event::ModifyKind::Metadata(_) => - { - let mod_time: std::time::SystemTime = - match get_file_mod_time(&paths[0]) - { - Ok(time) => { time } + match event + { + // This is just handling the events. On my system they came across as Any + // for the MetadataKind, but the documentation seemed to hint that + // modification times should be on write events. So for the sake of + // being done without testing this on different platforms the Any + // modification event will trigger a time lookup. + notify::event::ModifyKind::Any | + notify::event::ModifyKind::Metadata(_) => + { + let mod_time: std::time::SystemTime = match get_file_mod_time(&paths + [0]) + { + Ok(time) => time, - Err(e) => - { - eprintln!("Unable to open file to retrieve modification time: {}", + Err(e) => + { + eprintln!("Unable to open file to retrieve modification time: {}", e); - std::time::SystemTime::now() - } - }; - - Some(crate::event::Event::Modify - { - path: paths[0].clone(), - mod_time: mod_time - }) - } - - // Handling a rename event so that during testing when a file is - // moved it is still tracked. - notify::event::ModifyKind::Name(mode) => - { - match mode - { - // This is for when a file is renamed and both the to and from - // are in the monitored directories. - notify::event::RenameMode::Both => - { - let mod_time: std::time::SystemTime = - match get_file_mod_time(&paths[1]) - { - Ok(time) => { time } - - Err(e) => - { - eprintln!("Unable to open file to retrieve modification time: {}", - e); - std::time::SystemTime::now() - } - }; - - Some(crate::event::Event::Rename - { - from: paths[0].clone(), - to: paths[1].clone(), - mod_time: mod_time - }) - } - - // This is for when a file is renamed and only the from - // file is in the monitored directories. - notify::event::RenameMode::From => - { - Some(crate::event::Event::Delete - { - path: paths[0].clone(), - }) - } - - // This is for when a file is renamed and both the to file - // is in the monitored directories. - notify::event::RenameMode::To => - { - let mod_time: std::time::SystemTime = - match get_file_mod_time(&paths[0]) - { - Ok(time) => { time } - - Err(e) => - { - eprintln!("Unable to open file to retrieve modification time: {}", - e); - std::time::SystemTime::now() - } - }; - - Some(crate::event::Event::New - { - path: paths[0].clone(), - mod_time: mod_time - }) - } - - _ => { None } + std::time::SystemTime::now() } - } + }; - _ => { None } - } + Some(crate::event::Event::Modify { path: paths[0].clone(), + mod_time: mod_time }) + } + + // Handling a rename event so that during testing when a file is + // moved it is still tracked. + notify::event::ModifyKind::Name(mode) => + { + match mode + { + // This is for when a file is renamed and both the to and from + // are in the monitored directories. + notify::event::RenameMode::Both => + { + let mod_time: std::time::SystemTime = + match get_file_mod_time(&paths[1]) + { + Ok(time) => time, + + Err(e) => + { + eprintln!("Unable to open file to retrieve \ + modification time: {}", + e); + std::time::SystemTime::now() + } + }; + + Some(crate::event::Event::Rename { from: paths[0].clone(), + to: paths[1].clone(), + mod_time: mod_time }) + } + + // This is for when a file is renamed and only the from + // file is in the monitored directories. + notify::event::RenameMode::From => + { + Some(crate::event::Event::Delete { path: paths[0].clone() }) + } + + // This is for when a file is renamed and both the to file + // is in the monitored directories. + notify::event::RenameMode::To => + { + let mod_time: std::time::SystemTime = + match get_file_mod_time(&paths[0]) + { + Ok(time) => time, + + Err(e) => + { + eprintln!("Unable to open file to retrieve \ + modification time: {}", + e); + std::time::SystemTime::now() + } + }; + + Some(crate::event::Event::New { path: paths[0].clone(), + mod_time: mod_time }) + } + + _ => None + } + } + + _ => None + } } /// Process the remove event into a Delete event. -fn process_remove_event(event: notify::event::RemoveKind, - paths: Vec) - -> Option +fn process_remove_event(event: notify::event::RemoveKind, paths: Vec) + -> Option { - if event == notify::event::RemoveKind::File - { - return Some(crate::event::Event::Delete - { - path: paths[0].clone(), - }); - } + if event == notify::event::RemoveKind::File + { + return Some(crate::event::Event::Delete { path: paths[0].clone() }); + } - None + None } diff --git a/src/util.rs b/src/util.rs index 8342c35..ad61e73 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,35 +2,35 @@ /// /// If it is unable to get the modification time from the file system /// it will default to the current system time. -pub fn get_file_mod_time(path: &std::path::Path) -> std::io::Result +pub fn get_file_mod_time(path: &std::path::Path) + -> std::io::Result { - let file = std::fs::File::open(path)?; - let meta = file.metadata()?; + let file = std::fs::File::open(path)?; + let meta = file.metadata()?; - if meta.is_file() - { - return Ok(meta.modified()?); - } + if meta.is_file() + { + return Ok(meta.modified()?); + } - Ok(std::time::SystemTime::now()) + Ok(std::time::SystemTime::now()) } -/// Create a new PathBuf that is the same as path but has the base removed if it can. -pub fn strip_path_prefix(path: &std::path::Path, base: &std::path::Path) -> std::path::PathBuf +/// Create a new PathBuf that is the same as path but has the base removed if it +/// can. +pub fn strip_path_prefix(path: &std::path::Path, base: &std::path::Path) + -> std::path::PathBuf { - match &path.strip_prefix(base) - { - Ok(rel_path) => - { - let mut final_path = std::path::PathBuf::new(); - final_path.push("./"); - final_path.push(rel_path); - final_path - } + match &path.strip_prefix(base) + { + Ok(rel_path) => + { + let mut final_path = std::path::PathBuf::new(); + final_path.push("./"); + final_path.push(rel_path); + final_path + } - Err(_) => - { - std::path::PathBuf::from(path) - } - } + Err(_) => std::path::PathBuf::from(path) + } }