notify_debouncer_full/
cache.rs

1use std::{
2    collections::HashMap,
3    path::{Path, PathBuf},
4};
5
6use file_id::{get_file_id, FileId};
7use notify::RecursiveMode;
8use walkdir::WalkDir;
9
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.
13pub 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.
17    fn cached_file_id(&self, path: &Path) -> Option<impl AsRef<FileId>>;
18
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.
22    fn add_path(&mut self, path: &Path, recursive_mode: RecursiveMode);
23
24    /// Remove a path from the cache.
25    ///
26    /// This will be called if a file or directory is deleted.
27    fn remove_path(&mut self, path: &Path);
28
29    /// Re-scan all `root_paths`.
30    ///
31    /// This will be called if the notification back-end has dropped events.
32    /// The root paths are passed as argument, so the implementer doesn't have to store them.
33    ///
34    /// The default implementation calls `add_path` for each root path.
35    fn rescan(&mut self, root_paths: &[(PathBuf, RecursiveMode)]) {
36        for (path, recursive_mode) in root_paths {
37            self.add_path(path, *recursive_mode);
38        }
39    }
40}
41
42/// A cache to hold the file system IDs of all watched files.
43///
44/// The file ID cache uses unique file IDs provided by the file system and is used to stitch together
45/// rename events in case the notification back-end doesn't emit rename cookies.
46#[derive(Debug, Clone, Default)]
47pub struct FileIdMap {
48    paths: HashMap<PathBuf, FileId>,
49}
50
51impl FileIdMap {
52    /// Construct an empty cache.
53    pub fn new() -> Self {
54        Default::default()
55    }
56
57    fn dir_scan_depth(is_recursive: bool) -> usize {
58        if is_recursive {
59            usize::MAX
60        } else {
61            1
62        }
63    }
64}
65
66impl FileIdCache for FileIdMap {
67    fn cached_file_id(&self, path: &Path) -> Option<impl AsRef<FileId>> {
68        self.paths.get(path)
69    }
70
71    fn add_path(&mut self, path: &Path, recursive_mode: RecursiveMode) {
72        let is_recursive = recursive_mode == RecursiveMode::Recursive;
73
74        for (path, file_id) in WalkDir::new(path)
75            .follow_links(true)
76            .max_depth(Self::dir_scan_depth(is_recursive))
77            .into_iter()
78            .filter_map(|entry| {
79                let path = entry.ok()?.into_path();
80                let file_id = get_file_id(&path).ok()?;
81                Some((path, file_id))
82            })
83        {
84            self.paths.insert(path, file_id);
85        }
86    }
87
88    fn remove_path(&mut self, path: &Path) {
89        self.paths.retain(|p, _| !p.starts_with(path));
90    }
91}
92
93/// An implementation of the `FileIdCache` trait that doesn't hold any data.
94///
95/// This pseudo cache can be used to disable the file tracking using file system IDs.
96#[derive(Debug, Clone, Default)]
97pub struct NoCache;
98
99impl NoCache {
100    /// Construct an empty cache.
101    pub fn new() -> Self {
102        Default::default()
103    }
104}
105
106impl FileIdCache for NoCache {
107    fn cached_file_id(&self, _path: &Path) -> Option<impl AsRef<FileId>> {
108        Option::<&FileId>::None
109    }
110
111    fn add_path(&mut self, _path: &Path, _recursive_mode: RecursiveMode) {}
112
113    fn remove_path(&mut self, _path: &Path) {}
114}
115
116/// The recommended file ID cache implementation for the current platform
117#[cfg(any(target_os = "linux", target_os = "android"))]
118pub type RecommendedCache = NoCache;
119/// The recommended file ID cache implementation for the current platform
120#[cfg(not(any(target_os = "linux", target_os = "android")))]
121pub type RecommendedCache = FileIdMap;