@@ -293,6 +293,20 @@ pub enum WatcherKind {
293293 NullWatcher ,
294294}
295295
296+ /// Providing methods for adding and removing paths to watch.
297+ ///
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 < ( ) > ;
302+
303+ /// Remove a path from watching. See [`Watcher::unwatch`] for more.
304+ fn remove ( & mut self , path : & Path ) -> Result < ( ) > ;
305+
306+ /// Ensure previously added/removed paths are applied.
307+ fn commit ( & mut self ) -> Result < ( ) > ;
308+ }
309+
296310/// Type that can deliver file activity notifications
297311///
298312/// `Watcher` is implemented per platform using the best implementation available on that platform.
@@ -328,6 +342,42 @@ pub trait Watcher {
328342 /// fails.
329343 fn unwatch ( & mut self , path : & Path ) -> Result < ( ) > ;
330344
345+ /// Add/remove paths to watch.
346+ ///
347+ /// 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.
348+ ///
349+ /// # Examples
350+ ///
351+ /// ```
352+ /// # use notify::{Watcher, RecursiveMode, Result};
353+ /// # use std::path::Path;
354+ /// # fn main() -> Result<()> {
355+ /// # let many_paths_to_add = vec![];
356+ /// let mut watcher = notify::recommended_watcher(|_event| { /* event handler */ })?;
357+ /// let mut watcher_paths = watcher.paths_mut();
358+ /// for path in many_paths_to_add {
359+ /// watcher_paths.add(path, RecursiveMode::Recursive)?;
360+ /// }
361+ /// watcher_paths.commit()?;
362+ /// # Ok(())
363+ /// # }
364+ /// ```
365+ fn paths_mut < ' me > ( & ' me mut self ) -> Box < dyn PathsMut + ' me > {
366+ struct DefaultPathsMut < ' a , T : ?Sized > ( & ' a mut T ) ;
367+ impl < ' a , T : Watcher + ?Sized > PathsMut for DefaultPathsMut < ' a , T > {
368+ fn add ( & mut self , path : & Path , recursive_mode : RecursiveMode ) -> Result < ( ) > {
369+ self . 0 . watch ( path, recursive_mode)
370+ }
371+ fn remove ( & mut self , path : & Path ) -> Result < ( ) > {
372+ self . 0 . unwatch ( path)
373+ }
374+ fn commit ( & mut self ) -> Result < ( ) > {
375+ Ok ( ( ) )
376+ }
377+ }
378+ Box :: new ( DefaultPathsMut ( self ) )
379+ }
380+
331381 /// Configure the watcher at runtime.
332382 ///
333383 /// See the [`Config`](config/struct.Config.html) struct for all configuration options.
@@ -392,7 +442,7 @@ where
392442#[ cfg( test) ]
393443mod tests {
394444 use std:: {
395- fs,
445+ fs, iter ,
396446 time:: { Duration , Instant } ,
397447 } ;
398448
@@ -425,6 +475,21 @@ mod tests {
425475 assert_debug_impl ! ( WatcherKind ) ;
426476 }
427477
478+ fn iter_with_timeout ( rx : & Receiver < Result < Event > > ) -> impl Iterator < Item = Event > + ' _ {
479+ // wait for up to 10 seconds for the events
480+ let deadline = Instant :: now ( ) + Duration :: from_secs ( 10 ) ;
481+ iter:: from_fn ( move || {
482+ if Instant :: now ( ) >= deadline {
483+ return None ;
484+ }
485+ Some (
486+ rx. recv_timeout ( deadline - Instant :: now ( ) )
487+ . expect ( "did not receive expected event" )
488+ . expect ( "received an error" ) ,
489+ )
490+ } )
491+ }
492+
428493 #[ test]
429494 fn integration ( ) -> std:: result:: Result < ( ) , Box < dyn std:: error:: Error > > {
430495 let dir = tempdir ( ) ?;
@@ -440,14 +505,8 @@ mod tests {
440505
441506 println ! ( "waiting for event at {}" , file_path. display( ) ) ;
442507
443- // wait for up to 10 seconds for the create event, ignore all other events
444- let deadline = Instant :: now ( ) + Duration :: from_secs ( 10 ) ;
445- while deadline > Instant :: now ( ) {
446- let event = rx
447- . recv_timeout ( deadline - Instant :: now ( ) )
448- . expect ( "did not receive expected event" )
449- . expect ( "received an error" ) ;
450-
508+ // wait for the create event, ignore all other events
509+ for event in iter_with_timeout ( & rx) {
451510 if event. paths == vec ! [ file_path. clone( ) ]
452511 || event. paths == vec ! [ file_path. canonicalize( ) ?]
453512 {
@@ -478,4 +537,76 @@ mod tests {
478537
479538 Ok ( ( ) )
480539 }
540+
541+ #[ test]
542+ fn test_paths_mut ( ) -> std:: result:: Result < ( ) , Box < dyn std:: error:: Error > > {
543+ let dir = tempdir ( ) ?;
544+
545+ let dir_a = dir. path ( ) . join ( "a" ) ;
546+ let dir_b = dir. path ( ) . join ( "b" ) ;
547+
548+ fs:: create_dir ( & dir_a) ?;
549+ fs:: create_dir ( & dir_b) ?;
550+
551+ let ( tx, rx) = std:: sync:: mpsc:: channel ( ) ;
552+ let mut watcher = RecommendedWatcher :: new ( tx, Config :: default ( ) ) ?;
553+
554+ // start watching a and b
555+ {
556+ let mut watcher_paths = watcher. paths_mut ( ) ;
557+ watcher_paths. add ( & dir_a, RecursiveMode :: Recursive ) ?;
558+ watcher_paths. add ( & dir_b, RecursiveMode :: Recursive ) ?;
559+ watcher_paths. commit ( ) ?;
560+ }
561+
562+ // create file1 in both a and b
563+ let a_file1 = dir_a. join ( "file1" ) ;
564+ let b_file1 = dir_b. join ( "file1" ) ;
565+ fs:: write ( & a_file1, b"Lorem ipsum" ) ?;
566+ fs:: write ( & b_file1, b"Lorem ipsum" ) ?;
567+
568+ // wait for create events of a/file1 and b/file1
569+ let mut a_file1_encountered: bool = false ;
570+ let mut b_file1_encountered: bool = false ;
571+ for event in iter_with_timeout ( & rx) {
572+ for path in event. paths {
573+ a_file1_encountered =
574+ a_file1_encountered || ( path == a_file1 || path == a_file1. canonicalize ( ) ?) ;
575+ b_file1_encountered =
576+ b_file1_encountered || ( path == b_file1 || path == b_file1. canonicalize ( ) ?) ;
577+ }
578+ if a_file1_encountered && b_file1_encountered {
579+ break ;
580+ }
581+ }
582+ assert ! ( a_file1_encountered, "Did not receive event of {a_file1:?}" ) ;
583+ assert ! ( b_file1_encountered, "Did not receive event of {b_file1:?}" ) ;
584+
585+ // stop watching a
586+ {
587+ let mut watcher_paths = watcher. paths_mut ( ) ;
588+ watcher_paths. remove ( & dir_a) ?;
589+ watcher_paths. commit ( ) ?;
590+ }
591+
592+ // create file2 in both a and b
593+ let a_file2 = dir_a. join ( "file2" ) ;
594+ let b_file2 = dir_b. join ( "file2" ) ;
595+ fs:: write ( & a_file2, b"Lorem ipsum" ) ?;
596+ fs:: write ( & b_file2, b"Lorem ipsum" ) ?;
597+
598+ // wait for the create event of b/file2 only
599+ for event in iter_with_timeout ( & rx) {
600+ for path in event. paths {
601+ assert ! (
602+ path != a_file2 || path != a_file2. canonicalize( ) ?,
603+ "Event of {a_file2:?} should not be received"
604+ ) ;
605+ if path == b_file2 || path == b_file2. canonicalize ( ) ? {
606+ return Ok ( ( ) ) ;
607+ }
608+ }
609+ }
610+ panic ! ( "Did not receive the event of {b_file2:?}" ) ;
611+ }
481612}
0 commit comments