Skip to main content

ed_journals/modules/fs/models/
file_watcher.rs

1use crate::fs::error::LogFSError;
2use crate::fs::traits::unblocker::Unblocker;
3use notify::event::{CreateKind, ModifyKind};
4use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher};
5use std::fmt::{Debug, Formatter};
6use std::path::Path;
7use std::sync::Arc;
8
9/// Watches a file for changes and unblocks the provided blocker when a change occurs.
10///
11/// ```rust
12/// use std::env::current_dir;
13/// use ed_journals::fs::{auto_detect_journal_path, FileWatcher, SyncBlocker};
14///
15/// let path = current_dir()
16///     .unwrap()
17///     .join("..")
18///     .join("test-files")
19///     .join("journals")
20///     .join("Journal.2000-01-01T100000.01.log");
21///
22/// let blocker = SyncBlocker::new();
23/// let file_watcher = FileWatcher::new(&path, &blocker).unwrap();
24///
25/// # return;
26/// blocker.wait().unwrap();
27/// // Something changed
28/// ```
29///
30/// Keep in mind that this watcher needs to be kept in scope for as long as you want to receive
31/// notifications.
32pub struct FileWatcher {
33    _watcher: RecommendedWatcher,
34}
35
36impl FileWatcher {
37    /// Creates a new [FileWatcher] which will watch the provided path for changes.
38    pub fn new<P: AsRef<Path>>(
39        path: P,
40        unblocker: impl Into<Arc<dyn Unblocker>>,
41    ) -> Result<FileWatcher, LogFSError> {
42        let unblocker = unblocker.into();
43
44        let mut watcher =
45            notify::recommended_watcher(move |event: notify::Result<notify::Event>| {
46                let event: notify::Event = match event {
47                    Ok(event) => event,
48                    Err(error) => {
49                        let _ = unblocker.unblock(Err(LogFSError::NotifyError(error)));
50                        return;
51                    }
52                };
53
54                #[cfg(target_family = "unix")]
55                match event.kind {
56                    EventKind::Create(CreateKind::File)
57                    | EventKind::Modify(ModifyKind::Data(_)) => true,
58                    _ => return,
59                };
60
61                #[cfg(target_family = "windows")]
62                match event.kind {
63                    EventKind::Create(CreateKind::Any) | EventKind::Modify(ModifyKind::Any) => true,
64                    _ => return,
65                };
66
67                let _ = unblocker.unblock(Ok(()));
68            })?;
69
70        watcher.watch(path.as_ref(), RecursiveMode::NonRecursive)?;
71
72        Ok(FileWatcher { _watcher: watcher })
73    }
74}
75
76impl Debug for FileWatcher {
77    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
78        write!(f, "FileWatcher")
79    }
80}