|
163 | 163 | #![deny(missing_docs)]
|
164 | 164 |
|
165 | 165 | pub use config::{Config, RecursiveMode};
|
166 |
| -pub use error::{Error, ErrorKind, Result}; |
| 166 | +pub use error::{Error, ErrorKind, Result, UpdateWatchesError}; |
167 | 167 | pub use notify_types::event::{self, Event, EventKind};
|
168 |
| -use std::path::Path; |
| 168 | +use std::path::{Path, PathBuf}; |
169 | 169 |
|
| 170 | +pub(crate) type StdResult<T, E> = std::result::Result<T, E>; |
170 | 171 | pub(crate) type Receiver<T> = std::sync::mpsc::Receiver<T>;
|
171 | 172 | pub(crate) type Sender<T> = std::sync::mpsc::Sender<T>;
|
172 | 173 | #[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
|
@@ -295,19 +296,56 @@ pub enum WatcherKind {
|
295 | 296 |
|
296 | 297 | /// Providing methods for adding and removing paths to watch.
|
297 | 298 | ///
|
298 |
| -/// `Box<dyn PathsMut>` is created by [`Watcher::paths_mut`]. See its documentation for more. |
299 |
| -pub trait PathsMut { |
300 |
| - /// Add a new path to watch. See [`Watcher::watch`] for more. |
301 |
| - fn add(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()>; |
| 299 | +/// It is created by [`Watcher::paths_mut`]. |
| 300 | +pub struct PathsMut<'a> { |
| 301 | + ops: Vec<WatchOp>, |
| 302 | + watcher: &'a mut dyn Watcher, |
| 303 | +} |
| 304 | + |
| 305 | +impl<'a> Extend<WatchOp> for PathsMut<'a> { |
| 306 | + fn extend<T: IntoIterator<Item = WatchOp>>(&mut self, iter: T) { |
| 307 | + self.ops.extend(iter); |
| 308 | + } |
| 309 | +} |
| 310 | + |
| 311 | +impl<'a> PathsMut<'a> { |
| 312 | + /// Creates new [`PathsMut`] to operate with watched paths in a bulk manner |
| 313 | + pub fn new<W: Watcher + 'a>(watcher: &'a mut W) -> Self { |
| 314 | + Self { |
| 315 | + ops: Default::default(), |
| 316 | + watcher, |
| 317 | + } |
| 318 | + } |
| 319 | + |
| 320 | + /// Plan a path to be added to watching. See [`Watcher::unwatch`] for more information. |
| 321 | + pub fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) { |
| 322 | + self.ops |
| 323 | + .push(WatchOp::Watch(path.to_owned(), recursive_mode)); |
| 324 | + } |
302 | 325 |
|
303 |
| - /// Remove a path from watching. See [`Watcher::unwatch`] for more. |
304 |
| - fn remove(&mut self, path: &Path) -> Result<()>; |
| 326 | + /// Plan a path to be removed from watching. See [`Watcher::unwatch`] for more information. |
| 327 | + pub fn unwatch(&mut self, path: &Path) { |
| 328 | + self.ops.push(WatchOp::Unwatch(path.to_owned())); |
| 329 | + } |
305 | 330 |
|
306 |
| - /// Ensure added/removed paths are applied. |
| 331 | + /// Commit all the operations. |
307 | 332 | ///
|
308 |
| - /// The behaviour of dropping a [`PathsMut`] without calling [`commit`] is unspecified. |
309 |
| - /// The implementation is free to ignore the changes or not, and may leave the watcher in a started or stopped state. |
310 |
| - fn commit(self: Box<Self>) -> Result<()>; |
| 333 | + /// Dropping a [`PathsMut`] without calling [`Self::commit`] is noop. |
| 334 | + pub fn commit(self) -> StdResult<(), UpdateWatchesError> { |
| 335 | + self.watcher.update_watches(self.ops) |
| 336 | + } |
| 337 | +} |
| 338 | + |
| 339 | +/// An operation to apply to a watcher |
| 340 | +/// |
| 341 | +/// See [`Watcher::paths_mut`] for more information |
| 342 | +#[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 343 | +pub enum WatchOp { |
| 344 | + /// Path should be watcher |
| 345 | + Watch(PathBuf, RecursiveMode), |
| 346 | + |
| 347 | + /// Path should be unwatched |
| 348 | + Unwatch(PathBuf), |
311 | 349 | }
|
312 | 350 |
|
313 | 351 | /// Type that can deliver file activity notifications
|
@@ -345,40 +383,47 @@ pub trait Watcher {
|
345 | 383 | /// fails.
|
346 | 384 | fn unwatch(&mut self, path: &Path) -> Result<()>;
|
347 | 385 |
|
348 |
| - /// Add/remove paths to watch. |
| 386 | + /// Add/remove paths to watch in batch. |
349 | 387 | ///
|
350 |
| - /// For some watcher implementations this method provides better performance than multiple calls to [`Watcher::watch`] and [`Watcher::unwatch`] if you want to add/remove many paths at once. |
| 388 | + /// It is ergonomic way to call [`Watcher::update_watches`] |
351 | 389 | ///
|
352 | 390 | /// # Examples
|
353 | 391 | ///
|
354 | 392 | /// ```
|
355 |
| - /// # use notify::{Watcher, RecursiveMode, Result}; |
| 393 | + /// # use notify::{Watcher, RecursiveMode}; |
356 | 394 | /// # use std::path::Path;
|
357 |
| - /// # fn main() -> Result<()> { |
| 395 | + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { |
358 | 396 | /// # let many_paths_to_add = vec![];
|
| 397 | + /// # let many_paths_to_remove = vec![]; |
359 | 398 | /// let mut watcher = notify::recommended_watcher(|_event| { /* event handler */ })?;
|
360 | 399 | /// let mut watcher_paths = watcher.paths_mut();
|
| 400 | + /// |
361 | 401 | /// for path in many_paths_to_add {
|
362 |
| - /// watcher_paths.add(path, RecursiveMode::Recursive)?; |
| 402 | + /// // just some memory stuff |
| 403 | + /// watcher_paths.watch(path, RecursiveMode::Recursive); |
363 | 404 | /// }
|
| 405 | + /// |
| 406 | + /// for path in many_paths_to_remove { |
| 407 | + /// // just some memory stuff |
| 408 | + /// watcher_paths.unwatch(path); |
| 409 | + /// } |
| 410 | + /// |
| 411 | + /// // real work is done there |
364 | 412 | /// watcher_paths.commit()?;
|
365 | 413 | /// # Ok(())
|
366 | 414 | /// # }
|
367 | 415 | /// ```
|
368 |
| - fn paths_mut<'me>(&'me mut self) -> Box<dyn PathsMut + 'me> { |
369 |
| - struct DefaultPathsMut<'a, T: ?Sized>(&'a mut T); |
370 |
| - impl<'a, T: Watcher + ?Sized> PathsMut for DefaultPathsMut<'a, T> { |
371 |
| - fn add(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> { |
372 |
| - self.0.watch(path, recursive_mode) |
373 |
| - } |
374 |
| - fn remove(&mut self, path: &Path) -> Result<()> { |
375 |
| - self.0.unwatch(path) |
376 |
| - } |
377 |
| - fn commit(self: Box<Self>) -> Result<()> { |
378 |
| - Ok(()) |
379 |
| - } |
380 |
| - } |
381 |
| - Box::new(DefaultPathsMut(self)) |
| 416 | + fn paths_mut(&mut self) -> crate::PathsMut<'_>; |
| 417 | + |
| 418 | + /// Add/remove paths to watch in batch. |
| 419 | + /// |
| 420 | + /// For some [`Watcher`] implementations this method provides better performance than multiple |
| 421 | + /// calls to [`Watcher::watch`] and [`Watcher::unwatch`] if you want to add/remove many paths at once. |
| 422 | + fn update_watches(&mut self, ops: Vec<WatchOp>) -> StdResult<(), UpdateWatchesError> { |
| 423 | + update_watches(ops, |op| match op { |
| 424 | + WatchOp::Watch(path, recursive_mode) => self.watch(&path, recursive_mode), |
| 425 | + WatchOp::Unwatch(path) => self.unwatch(&path), |
| 426 | + }) |
382 | 427 | }
|
383 | 428 |
|
384 | 429 | /// Configure the watcher at runtime.
|
@@ -442,6 +487,25 @@ where
|
442 | 487 | RecommendedWatcher::new(event_handler, Config::default())
|
443 | 488 | }
|
444 | 489 |
|
| 490 | +pub(crate) fn update_watches<F>( |
| 491 | + ops: Vec<WatchOp>, |
| 492 | + mut apply: F, |
| 493 | +) -> StdResult<(), UpdateWatchesError> |
| 494 | +where |
| 495 | + F: FnMut(WatchOp) -> Result<()>, |
| 496 | +{ |
| 497 | + let mut iter = ops.into_iter(); |
| 498 | + while let Some(op) = iter.next() { |
| 499 | + if let Err(source) = apply(op) { |
| 500 | + return Err(UpdateWatchesError { |
| 501 | + source, |
| 502 | + remaining: iter.collect(), |
| 503 | + }); |
| 504 | + } |
| 505 | + } |
| 506 | + Ok(()) |
| 507 | +} |
| 508 | + |
445 | 509 | #[cfg(test)]
|
446 | 510 | mod tests {
|
447 | 511 | use std::{
|
@@ -557,8 +621,8 @@ mod tests {
|
557 | 621 | // start watching a and b
|
558 | 622 | {
|
559 | 623 | let mut watcher_paths = watcher.paths_mut();
|
560 |
| - watcher_paths.add(&dir_a, RecursiveMode::Recursive)?; |
561 |
| - watcher_paths.add(&dir_b, RecursiveMode::Recursive)?; |
| 624 | + watcher_paths.watch(&dir_a, RecursiveMode::Recursive); |
| 625 | + watcher_paths.watch(&dir_b, RecursiveMode::Recursive); |
562 | 626 | watcher_paths.commit()?;
|
563 | 627 | }
|
564 | 628 |
|
@@ -588,7 +652,7 @@ mod tests {
|
588 | 652 | // stop watching a
|
589 | 653 | {
|
590 | 654 | let mut watcher_paths = watcher.paths_mut();
|
591 |
| - watcher_paths.remove(&dir_a)?; |
| 655 | + watcher_paths.unwatch(&dir_a); |
592 | 656 | watcher_paths.commit()?;
|
593 | 657 | }
|
594 | 658 |
|
|
0 commit comments