Skip to content

Commit 4fc2a10

Browse files
dfaust0xpr03
authored andcommitted
Improve docs
1 parent a2f5666 commit 4fc2a10

File tree

3 files changed

+106
-66
lines changed

3 files changed

+106
-66
lines changed

file-id/src/lib.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
/// A utility to read file IDs.
2-
///
3-
/// Modern file systems assign a unique ID to each file. On Linux and MacOS it is called an `inode number`, on Windows it is called `file index`.
4-
/// Together with the `device id`, a file can be identified uniquely on a device at a given time.
5-
///
6-
/// Keep in mind though, that IDs may be re-used at some point.
7-
///
8-
/// ## Example
9-
///
10-
/// ```rust
11-
/// # let file = tempfile::NamedTempFile::new().unwrap();
12-
/// # let path = file.path();
13-
///
14-
/// let file_id = file_id::get_file_id(path).unwrap();
15-
///
16-
/// println!("{file_id:?}");
17-
/// ```
1+
//! A utility to read file IDs that are unique on a given device.
2+
//!
3+
//! Modern file systems assign a unique ID to each file. On Linux and MacOS it is called an `inode number`, on Windows it is called a `file index`.
4+
//! Together with the `device id`, a file can be identified uniquely on a device at a given time.
5+
//!
6+
//! Keep in mind though, that IDs may be re-used at some point.
7+
//!
8+
//! ## Example
9+
//!
10+
//! ```rust
11+
//! let file = tempfile::NamedTempFile::new().unwrap();
12+
//!
13+
//! let file_id = file_id::get_file_id(file.path()).unwrap();
14+
//!
15+
//! println!("{file_id:?}");
16+
//! ```
1817
use std::{fs, io, path::Path};
1918

2019
#[cfg(feature = "serde")]

notify-debouncer-refined/src/cache.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,52 @@ use file_id::{get_file_id, FileId};
77
use notify::RecursiveMode;
88
use walkdir::WalkDir;
99

10+
/// The interface of a file ID cache.
11+
///
12+
/// This trait can be implemented for an existing cache, if it already holds `FileId`s.
1013
pub trait FileIdCache {
14+
/// Get a `FileId` from the cache for a given `path`.
15+
///
16+
/// If the path is not cached, `None` should be returned and there should not be any attempt to read the file ID from disk.
1117
fn cached_file_id(&self, path: &Path) -> Option<&FileId>;
1218

19+
/// Add a new path to the cache or update its value.
20+
///
21+
/// This will be called if a new file or directory is created or if an existing file is overridden.
1322
fn add_path(&mut self, path: &Path);
1423

24+
/// Remove a path from the cache.
25+
///
26+
/// This will be called if a file or directory is deleted.
1527
fn remove_path(&mut self, path: &Path);
1628

29+
/// Re-scan all paths.
30+
///
31+
/// This will be called if the notification back-end has dropped events.
1732
fn rescan(&mut self);
1833
}
1934

35+
/// A cache to hold the file system IDs of all watched files.
36+
///
37+
/// The file ID cache uses unique file IDs provided by the file system and is used to stich together
38+
/// rename events in case the notification back-end doesn't emit rename cookies.
2039
#[derive(Debug, Clone, Default)]
21-
pub struct PathCache {
40+
pub struct FileIdMap {
2241
paths: HashMap<PathBuf, FileId>,
2342
roots: Vec<(PathBuf, RecursiveMode)>,
2443
}
2544

26-
impl PathCache {
45+
impl FileIdMap {
46+
/// Construct an empty cache.
47+
pub fn new() -> Self {
48+
Default::default()
49+
}
50+
51+
/// Add a path to the cache.
52+
///
53+
/// If `recursive_mode` is `Recursive`, all children will be added to the cache as well
54+
/// and all paths will be kept up-to-date in case of changes like new files being added,
55+
/// files being removed or renamed.
2756
pub fn add_root(&mut self, path: impl Into<PathBuf>, recursive_mode: RecursiveMode) {
2857
let path = path.into();
2958

@@ -32,6 +61,9 @@ impl PathCache {
3261
self.add_path(&path);
3362
}
3463

64+
/// Remove a path form the cache.
65+
///
66+
/// If the path was added with `Recursive` mode, all children will also be removed from the cache.
3567
pub fn remove_root(&mut self, path: impl AsRef<Path>) {
3668
self.roots.retain(|(root, _)| !root.starts_with(&path));
3769

@@ -47,7 +79,7 @@ impl PathCache {
4779
}
4880
}
4981

50-
impl FileIdCache for PathCache {
82+
impl FileIdCache for FileIdMap {
5183
fn cached_file_id(&self, path: &Path) -> Option<&FileId> {
5284
self.paths.get(path)
5385
}
@@ -90,6 +122,9 @@ impl FileIdCache for PathCache {
90122
}
91123
}
92124

125+
/// An implementation of the `FileIdCache` trait that doesn't hold any data.
126+
///
127+
/// This pseudo cache can be used to disable the file tracking using file system IDs.
93128
pub struct NoCache;
94129

95130
impl FileIdCache for NoCache {

notify-debouncer-refined/src/lib.rs

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
//! Debouncer for notify
1+
//! A debouncer for [notify] that is optimized for ease of use.
2+
//!
3+
//! * Only emits a single `Rename` event if the rename `From` and `To` events can be matched
4+
//! * Merges multiple `Rename` events
5+
//! * Optionally keeps track of the file system IDs all files and stiches rename events together (FSevents, Windows)
6+
//! * Emits only one `Remove` event when deleting a directory (inotify)
7+
//! * Doesn't emit duplicate create events
8+
//! * Doesn't emit `Modify` events after a `Create` event
29
//!
310
//! # Installation
411
//!
512
//! ```toml
613
//! [dependencies]
7-
//! notify-debouncer-easy = "0.2.0"
14+
//! notify-debouncer-refined = "0.1.0"
815
//! ```
16+
//!
917
//! In case you want to select specific features of notify,
1018
//! specify notify as dependency explicitely in your dependencies.
1119
//! Otherwise you can just use the re-export of notify from debouncer-easy.
20+
//!
1221
//! ```toml
13-
//! notify-debouncer-easy = "0.2.0"
22+
//! notify-debouncer-refined = "0.1.0"
1423
//! notify = { version = "..", features = [".."] }
1524
//! ```
1625
//!
@@ -21,20 +30,23 @@
2130
//! # use std::time::Duration;
2231
//! use notify_debouncer_refined::{notify::*, new_debouncer, DebounceEventResult};
2332
//!
24-
//! # fn main() {
25-
//! // Select recommended watcher for debouncer.
26-
//! // Using a callback here, could also be a channel.
27-
//! let mut debouncer = new_debouncer(Duration::from_secs(2), None, |result: DebounceEventResult| {
28-
//! match result {
29-
//! Ok(events) => events.iter().for_each(|event| println!("{event:?}")),
30-
//! Err(errors) => errors.iter().for_each(|error| println!("{error:?}")),
31-
//! }
32-
//! }).unwrap();
33+
//! // Select recommended watcher for debouncer.
34+
//! // Using a callback here, could also be a channel.
35+
//! let mut debouncer = new_debouncer(Duration::from_secs(2), None, |result: DebounceEventResult| {
36+
//! match result {
37+
//! Ok(events) => events.iter().for_each(|event| println!("{event:?}")),
38+
//! Err(errors) => errors.iter().for_each(|error| println!("{error:?}")),
39+
//! }
40+
//! }).unwrap();
3341
//!
34-
//! // Add a path to be watched. All files and directories at that path and
35-
//! // below will be monitored for changes.
36-
//! debouncer.watcher().watch(Path::new("."), RecursiveMode::Recursive).unwrap();
37-
//! # }
42+
//! // Add a path to be watched. All files and directories at that path and
43+
//! // below will be monitored for changes.
44+
//! debouncer.watcher().watch(Path::new("."), RecursiveMode::Recursive).unwrap();
45+
//!
46+
//! // Add the same path to the file ID cache. The cache uses unique file IDs
47+
//! // provided by the file system and is used to stich together rename events
48+
//! // in case the notification back-end doesn't emit rename cookies.
49+
//! debouncer.cache().add_root(Path::new("."), RecursiveMode::Recursive);
3850
//! ```
3951
//!
4052
//! # Features
@@ -59,7 +71,7 @@ use std::{
5971
time::Duration,
6072
};
6173

62-
pub use cache::{FileIdCache, PathCache};
74+
pub use cache::{FileIdCache, FileIdMap, NoCache};
6375

6476
pub use file_id;
6577
pub use notify;
@@ -166,7 +178,6 @@ impl Queue {
166178
}
167179
}
168180

169-
#[derive(Default)]
170181
pub(crate) struct DebounceDataInner<T> {
171182
queues: HashMap<PathBuf, Queue>,
172183
cache: T,
@@ -177,6 +188,17 @@ pub(crate) struct DebounceDataInner<T> {
177188
}
178189

179190
impl<T: FileIdCache> DebounceDataInner<T> {
191+
pub(crate) fn new(cache: T, timeout: Duration) -> Self {
192+
Self {
193+
queues: HashMap::new(),
194+
cache,
195+
rename_event: None,
196+
rescan_event: None,
197+
errors: Vec::new(),
198+
timeout,
199+
}
200+
}
201+
180202
/// Retrieve a vec of debounced events, removing them if not continuous
181203
pub fn debounced_events(&mut self) -> Vec<DebouncedEvent> {
182204
let now = Instant::now();
@@ -458,14 +480,6 @@ impl<T: FileIdCache> DebounceDataInner<T> {
458480
let path = &event.paths[0];
459481

460482
if let Some(queue) = self.queues.get_mut(path) {
461-
// if matches!(
462-
// event.kind,
463-
// EventKind::Modify(ModifyKind::Data(_) | ModifyKind::Metadata(_))
464-
// | EventKind::Access(_)
465-
// ) {
466-
// queue.events.retain(|(_, e)| e.kind != event.kind);
467-
// }
468-
469483
// skip duplicate create events and modifications right after creation
470484
if match event.kind {
471485
EventKind::Modify(ModifyKind::Data(_) | ModifyKind::Metadata(_))
@@ -485,7 +499,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
485499
}
486500
}
487501

488-
/// Debouncer guard, stops the debouncer on drop
502+
/// Debouncer guard, stops the debouncer on drop.
489503
pub struct Debouncer<T: Watcher, C: FileIdCache> {
490504
watcher: T,
491505
debouncer_thread: Option<std::thread::JoinHandle<()>>,
@@ -531,21 +545,17 @@ impl<T: Watcher, C: FileIdCache> Drop for Debouncer<T, C> {
531545

532546
/// Creates a new debounced watcher with custom configuration.
533547
///
534-
/// Timeout is the amount of time after which a debounced event is emitted or a continuous event is send, if there still are events incoming for the specific path.
548+
/// Timeout is the amount of time after which a debounced event is emitted.
535549
///
536-
/// If tick_rate is None, notify will select a tick rate that is less than the provided timeout.
537-
pub fn new_debouncer_opt<
538-
F: DebounceEventHandler,
539-
T: Watcher,
540-
C: FileIdCache + Default + Send + 'static,
541-
>(
550+
/// If tick_rate is None, notify will select a tick rate that is 1/4 of the provided timeout.
551+
pub fn new_debouncer_opt<F: DebounceEventHandler, T: Watcher, C: FileIdCache + Send + 'static>(
542552
timeout: Duration,
543553
tick_rate: Option<Duration>,
544554
mut event_handler: F,
555+
file_id_cache: C,
545556
config: notify::Config,
546557
) -> Result<Debouncer<T, C>, Error> {
547-
let data = DebounceData::<C>::default();
548-
558+
let data = Arc::new(Mutex::new(DebounceDataInner::new(file_id_cache, timeout)));
549559
let stop = Arc::new(AtomicBool::new(false));
550560

551561
let tick_div = 4;
@@ -567,11 +577,6 @@ pub fn new_debouncer_opt<
567577
})?,
568578
};
569579

570-
{
571-
let mut data_w = data.lock();
572-
data_w.timeout = timeout;
573-
}
574-
575580
let data_c = data.clone();
576581
let stop_c = stop.clone();
577582
let thread = std::thread::Builder::new()
@@ -620,20 +625,21 @@ pub fn new_debouncer_opt<
620625
Ok(guard)
621626
}
622627

623-
/// Short function to create a new debounced watcher with the recommended debouncer.
628+
/// Short function to create a new debounced watcher with the recommended debouncer and the built-in file ID cache.
624629
///
625-
/// Timeout is the amount of time after which a debounced event is emitted or a continuous event is send, if there still are events incoming for the specific path.
630+
/// Timeout is the amount of time after which a debounced event is emitted.
626631
///
627-
/// If tick_rate is None, notify will select a tick rate that is less than the provided timeout.
632+
/// If tick_rate is None, notify will select a tick rate that is 1/4 of the provided timeout.
628633
pub fn new_debouncer<F: DebounceEventHandler>(
629634
timeout: Duration,
630635
tick_rate: Option<Duration>,
631636
event_handler: F,
632-
) -> Result<Debouncer<RecommendedWatcher, PathCache>, Error> {
633-
new_debouncer_opt::<F, RecommendedWatcher, PathCache>(
637+
) -> Result<Debouncer<RecommendedWatcher, FileIdMap>, Error> {
638+
new_debouncer_opt::<F, RecommendedWatcher, FileIdMap>(
634639
timeout,
635640
tick_rate,
636641
event_handler,
642+
FileIdMap::new(),
637643
notify::Config::default(),
638644
)
639645
}

0 commit comments

Comments
 (0)