Skip to main content

notify/
config.rs

1//! Configuration types
2
3use std::time::Duration;
4
5/// Default maximum number of paths to pass to FSEvents, chosen to stay
6/// well under the macOS default file descriptor soft limit (256).
7pub const DEFAULT_MAX_FSEVENT_PATHS: usize = 128;
8
9/// Indicates how the path should be watched
10#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
11pub struct WatchMode {
12    /// Indicates whether to watch sub-directories as well
13    pub recursive_mode: RecursiveMode,
14    /// Indicates what happens when the relationship of the physical entity and the file path changes
15    pub target_mode: TargetMode,
16}
17
18impl WatchMode {
19    /// Creates a WatchMode that watches directories recursively and tracks the file path
20    #[must_use]
21    pub fn recursive() -> Self {
22        Self {
23            recursive_mode: RecursiveMode::Recursive,
24            target_mode: TargetMode::TrackPath,
25        }
26    }
27
28    /// Creates a WatchMode that watches only the provided directory and tracks the file path
29    #[must_use]
30    pub fn non_recursive() -> Self {
31        Self {
32            recursive_mode: RecursiveMode::NonRecursive,
33            target_mode: TargetMode::TrackPath,
34        }
35    }
36
37    pub(crate) fn upgrade_with(&mut self, other: WatchMode) {
38        self.recursive_mode = self.recursive_mode.upgraded_with(other.recursive_mode);
39        self.target_mode = self.target_mode.upgraded_with(other.target_mode);
40    }
41}
42
43/// Indicates whether only the provided directory or its sub-directories as well should be watched
44#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
45pub enum RecursiveMode {
46    /// Watch all sub-directories as well, including directories created after installing the watch
47    Recursive,
48
49    /// Watch only the provided directory
50    NonRecursive,
51}
52
53impl RecursiveMode {
54    #[expect(clippy::trivially_copy_pass_by_ref)]
55    pub(crate) fn is_recursive(&self) -> bool {
56        match *self {
57            RecursiveMode::Recursive => true,
58            RecursiveMode::NonRecursive => false,
59        }
60    }
61
62    pub(crate) fn upgraded_with(self, other: Self) -> Self {
63        match self {
64            RecursiveMode::Recursive => self,
65            RecursiveMode::NonRecursive => {
66                if other == RecursiveMode::Recursive {
67                    other
68                } else {
69                    self
70                }
71            }
72        }
73    }
74}
75
76/// Indicates what happens when the relationship of the physical entity and the file path changes
77#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
78pub enum TargetMode {
79    /// Tracks the file path.
80    ///
81    /// If the underlying physical entity (inode/File ID) at this path is replaced
82    /// (e.g., by a move/rename operation), the watch continues to monitor the new entity
83    /// that now occupies the path.
84    ///
85    /// TODO: watching nested non-existent paths is not implemented yet. <https://github.com/rolldown/notify/issues/32>
86    TrackPath,
87
88    /// Does not track the file path, nor the physical entity.
89    ///
90    /// If the underlying physical entity (inode/File ID) is replaced
91    /// (e.g., by a move/rename operation), the watch stops monitoring.
92    ///
93    /// TODO: fsevents backend and Windows backend and polling backend does not unwatch on physical entity change yet. <https://github.com/rolldown/notify/issues/33>
94    NoTrack,
95}
96
97impl TargetMode {
98    pub(crate) fn upgraded_with(self, other: Self) -> Self {
99        match self {
100            TargetMode::TrackPath => self,
101            TargetMode::NoTrack => {
102                if other == TargetMode::TrackPath {
103                    other
104                } else {
105                    self
106                }
107            }
108        }
109    }
110}
111
112/// Watcher Backend configuration
113///
114/// This contains multiple settings that may relate to only one specific backend,
115/// such as to correctly configure each backend regardless of what is selected during runtime.
116///
117/// ```rust
118/// # use std::time::Duration;
119/// # use notify::Config;
120/// let config = Config::default()
121///     .with_poll_interval(Duration::from_secs(2))
122///     .with_compare_contents(true);
123/// ```
124///
125/// Some options can be changed during runtime, others have to be set when creating the watcher backend.
126#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
127pub struct Config {
128    /// See [Config::with_poll_interval]
129    poll_interval: Option<Duration>,
130
131    /// See [Config::with_compare_contents]
132    compare_contents: bool,
133
134    follow_symlinks: bool,
135
136    /// See [Config::with_max_fsevent_paths]
137    max_fsevent_paths: usize,
138}
139
140impl Config {
141    /// For the [`PollWatcher`](crate::PollWatcher) backend.
142    ///
143    /// Interval between each re-scan attempt. This can be extremely expensive for large
144    /// file trees so it is recommended to measure and tune accordingly.
145    ///
146    /// The default poll frequency is 30 seconds.
147    ///
148    /// This will enable automatic polling, overwriting [`with_manual_polling()`](Config::with_manual_polling).
149    #[must_use]
150    pub fn with_poll_interval(mut self, dur: Duration) -> Self {
151        // TODO: v7.0 break signature to option
152        self.poll_interval = Some(dur);
153        self
154    }
155
156    /// Returns current setting
157    #[must_use]
158    pub fn poll_interval(&self) -> Option<Duration> {
159        // Changed Signature to Option
160        self.poll_interval
161    }
162
163    /// For the [`PollWatcher`](crate::PollWatcher) backend.
164    ///
165    /// Disable automatic polling. Requires calling [`crate::PollWatcher::poll()`] manually.
166    ///
167    /// This will disable automatic polling, overwriting [`with_poll_interval()`](Config::with_poll_interval).
168    #[must_use]
169    pub fn with_manual_polling(mut self) -> Self {
170        self.poll_interval = None;
171        self
172    }
173
174    /// For the [`PollWatcher`](crate::PollWatcher) backend.
175    ///
176    /// Optional feature that will evaluate the contents of changed files to determine if
177    /// they have indeed changed using a fast hashing algorithm.  This is especially important
178    /// for pseudo filesystems like those on Linux under /sys and /proc which are not obligated
179    /// to respect any other filesystem norms such as modification timestamps, file sizes, etc.
180    /// By enabling this feature, performance will be significantly impacted as all files will
181    /// need to be read and hashed at each `poll_interval`.
182    ///
183    /// This can't be changed during runtime. Off by default.
184    #[must_use]
185    pub fn with_compare_contents(mut self, compare_contents: bool) -> Self {
186        self.compare_contents = compare_contents;
187        self
188    }
189
190    /// Returns current setting
191    #[must_use]
192    pub fn compare_contents(&self) -> bool {
193        self.compare_contents
194    }
195
196    /// For the [INotifyWatcher](crate::INotifyWatcher), [KqueueWatcher](crate::KqueueWatcher),
197    /// and [PollWatcher](crate::PollWatcher).
198    ///
199    /// Determine if symbolic links should be followed when recursively watching a directory.
200    ///
201    /// This can't be changed during runtime. On by default.
202    #[must_use]
203    pub fn with_follow_symlinks(mut self, follow_symlinks: bool) -> Self {
204        self.follow_symlinks = follow_symlinks;
205        self
206    }
207
208    /// Returns current setting
209    #[must_use]
210    pub fn follow_symlinks(&self) -> bool {
211        self.follow_symlinks
212    }
213
214    /// For the [`FsEventWatcher`](crate::FsEventWatcher) backend.
215    ///
216    /// Maximum number of paths to pass to FSEvents. When the number of
217    /// watched paths exceeds this limit, the watcher automatically watches
218    /// parent directories instead of individual paths to reduce file
219    /// descriptor usage.
220    ///
221    /// The default is [`DEFAULT_MAX_FSEVENT_PATHS`]. Set to `0` to disable
222    /// consolidation.
223    ///
224    /// This can't be changed during runtime.
225    #[must_use]
226    pub fn with_max_fsevent_paths(mut self, max_paths: usize) -> Self {
227        self.max_fsevent_paths = max_paths;
228        self
229    }
230
231    /// Returns current setting.
232    ///
233    /// `0` means consolidation is disabled.
234    #[must_use]
235    pub fn max_fsevent_paths(&self) -> usize {
236        self.max_fsevent_paths
237    }
238}
239
240impl Default for Config {
241    fn default() -> Self {
242        Self {
243            poll_interval: Some(Duration::from_secs(30)),
244            compare_contents: false,
245            follow_symlinks: true,
246            max_fsevent_paths: DEFAULT_MAX_FSEVENT_PATHS,
247        }
248    }
249}