4949//!
5050//! The following crate features can be turned on or off in your cargo dependency config:
5151//!
52- //! - `crossbeam` passed down to notify, off by default
52+ //! - `crossbeam-channel ` passed down to notify, off by default
5353//! - `serialization-compat-6` passed down to notify, off by default
5454//!
5555//! # Caveats
5656//!
5757//! As all file events are sourced from notify, the [known problems](https://docs.rs/notify/latest/notify/#known-problems) section applies here too.
5858
5959mod cache;
60+ mod time;
6061
6162#[ cfg( test) ]
6263mod testing;
@@ -69,9 +70,11 @@ use std::{
6970 atomic:: { AtomicBool , Ordering } ,
7071 Arc , Mutex ,
7172 } ,
72- time:: Duration ,
73+ time:: { Duration , Instant } ,
7374} ;
7475
76+ use time:: now;
77+
7578pub use cache:: { FileIdCache , FileIdMap , NoCache , RecommendedCache } ;
7679
7780pub use file_id;
@@ -84,12 +87,6 @@ use notify::{
8487 Error , ErrorKind , Event , EventKind , RecommendedWatcher , RecursiveMode , Watcher , WatcherKind ,
8588} ;
8689
87- #[ cfg( feature = "mock_instant" ) ]
88- use mock_instant:: Instant ;
89-
90- #[ cfg( not( feature = "mock_instant" ) ) ]
91- use std:: time:: Instant ;
92-
9390/// The set of requirements for watcher debounce event handling functions.
9491///
9592/// # Example implementation
@@ -124,7 +121,7 @@ where
124121 }
125122}
126123
127- #[ cfg( feature = "crossbeam" ) ]
124+ #[ cfg( feature = "crossbeam-channel " ) ]
128125impl DebounceEventHandler for crossbeam_channel:: Sender < DebounceEventResult > {
129126 fn handle_event ( & mut self , event : DebounceEventResult ) {
130127 let _ = self . send ( event) ;
@@ -198,7 +195,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
198195
199196 /// Retrieve a vec of debounced events, removing them if not continuous
200197 pub fn debounced_events ( & mut self ) -> Vec < DebouncedEvent > {
201- let now = Instant :: now ( ) ;
198+ let now = now ( ) ;
202199 let mut events_expired = Vec :: with_capacity ( self . queues . len ( ) ) ;
203200 let mut queues_remaining = HashMap :: with_capacity ( self . queues . len ( ) ) ;
204201
@@ -211,6 +208,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
211208 }
212209 }
213210
211+ // drain the entire queue, then process the expired events and re-add the rest
214212 // TODO: perfect fit for drain_filter https://github.com/rust-lang/rust/issues/59618
215213 for ( path, mut queue) in self . queues . drain ( ) {
216214 let mut kind_index = HashMap :: new ( ) ;
@@ -249,13 +247,13 @@ impl<T: FileIdCache> DebounceDataInner<T> {
249247
250248 /// Returns all currently stored errors
251249 pub fn errors ( & mut self ) -> Vec < Error > {
252- let mut v = Vec :: new ( ) ;
253- std:: mem:: swap ( & mut v, & mut self . errors ) ;
254- v
250+ std:: mem:: take ( & mut self . errors )
255251 }
256252
257253 /// Add an error entry to re-send later on
258254 pub fn add_error ( & mut self , error : Error ) {
255+ log:: trace!( "raw error: {error:?}" ) ;
256+
259257 self . errors . push ( error) ;
260258 }
261259
@@ -265,7 +263,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
265263
266264 if event. need_rescan ( ) {
267265 self . cache . rescan ( & self . roots ) ;
268- self . rescan_event = Some ( event. into ( ) ) ;
266+ self . rescan_event = Some ( DebouncedEvent { event, time : now ( ) } ) ;
269267 return ;
270268 }
271269
@@ -277,7 +275,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
277275
278276 self . cache . add_path ( path, recursive_mode) ;
279277
280- self . push_event ( event, Instant :: now ( ) ) ;
278+ self . push_event ( event, now ( ) ) ;
281279 }
282280 EventKind :: Modify ( ModifyKind :: Name ( rename_mode) ) => {
283281 match rename_mode {
@@ -303,7 +301,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
303301 }
304302 }
305303 EventKind :: Remove ( _) => {
306- self . push_remove_event ( event, Instant :: now ( ) ) ;
304+ self . push_remove_event ( event, now ( ) ) ;
307305 }
308306 EventKind :: Other => {
309307 // ignore meta events
@@ -315,7 +313,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
315313 self . cache . add_path ( path, recursive_mode) ;
316314 }
317315
318- self . push_event ( event, Instant :: now ( ) ) ;
316+ self . push_event ( event, now ( ) ) ;
319317 }
320318 }
321319 }
@@ -334,7 +332,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
334332 }
335333
336334 fn handle_rename_from ( & mut self , event : Event ) {
337- let time = Instant :: now ( ) ;
335+ let time = now ( ) ;
338336 let path = & event. paths [ 0 ] ;
339337
340338 // store event
@@ -382,7 +380,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
382380 self . push_rename_event ( path, event, time) ;
383381 } else {
384382 // move in
385- self . push_event ( event, Instant :: now ( ) ) ;
383+ self . push_event ( event, now ( ) ) ;
386384 }
387385
388386 self . rename_event = None ;
@@ -753,10 +751,11 @@ mod tests {
753751
754752 use super :: * ;
755753
756- use mock_instant:: MockClock ;
757754 use pretty_assertions:: assert_eq;
758755 use rstest:: rstest;
756+ use tempfile:: tempdir;
759757 use testing:: TestCase ;
758+ use time:: MockTime ;
760759
761760 #[ rstest]
762761 fn state (
@@ -805,16 +804,19 @@ mod tests {
805804 fs:: read_to_string ( Path :: new ( & format ! ( "./test_cases/{file_name}.hjson" ) ) ) . unwrap ( ) ;
806805 let mut test_case = deser_hjson:: from_str :: < TestCase > ( & file_content) . unwrap ( ) ;
807806
808- MockClock :: set_time ( Duration :: default ( ) ) ;
809-
810- let time = Instant :: now ( ) ;
807+ let time = now ( ) ;
808+ MockTime :: set_time ( time) ;
811809
812810 let mut state = test_case. state . into_debounce_data_inner ( time) ;
813811 state. roots = vec ! [ ( PathBuf :: from( "/" ) , RecursiveMode :: Recursive ) ] ;
814812
813+ let mut prev_event_time = Duration :: default ( ) ;
814+
815815 for event in test_case. events {
816+ let event_time = Duration :: from_millis ( event. time ) ;
816817 let event = event. into_debounced_event ( time, None ) ;
817- MockClock :: set_time ( event. time - time) ;
818+ MockTime :: advance ( event_time - prev_event_time) ;
819+ prev_event_time = event_time;
818820 state. add_event ( event. event ) ;
819821 }
820822
@@ -856,21 +858,20 @@ mod tests {
856858 "errors not as expected"
857859 ) ;
858860
859- let backup_time = Instant :: now ( ) . duration_since ( time ) ;
861+ let backup_time = now ( ) ;
860862 let backup_queues = state. queues . clone ( ) ;
861863
862864 for ( delay, events) in expected_events {
863- MockClock :: set_time ( backup_time) ;
865+ MockTime :: set_time ( backup_time) ;
864866 state. queues = backup_queues. clone ( ) ;
865867
866868 match delay. as_str ( ) {
867869 "none" => { }
868- "short" => MockClock :: advance ( Duration :: from_millis ( 10 ) ) ,
869- "long" => MockClock :: advance ( Duration :: from_millis ( 100 ) ) ,
870+ "short" => MockTime :: advance ( Duration :: from_millis ( 10 ) ) ,
871+ "long" => MockTime :: advance ( Duration :: from_millis ( 100 ) ) ,
870872 _ => {
871873 if let Ok ( ts) = delay. parse :: < u64 > ( ) {
872- let ts = time + Duration :: from_millis ( ts) ;
873- MockClock :: set_time ( ts - time) ;
874+ MockTime :: set_time ( time + Duration :: from_millis ( ts) ) ;
874875 }
875876 }
876877 }
@@ -887,4 +888,26 @@ mod tests {
887888 ) ;
888889 }
889890 }
891+
892+ #[ test]
893+ fn integration ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
894+ let dir = tempdir ( ) ?;
895+
896+ let ( tx, rx) = std:: sync:: mpsc:: channel ( ) ;
897+
898+ let mut debouncer = new_debouncer ( Duration :: from_millis ( 10 ) , None , tx) ?;
899+
900+ debouncer. watch ( dir. path ( ) , RecursiveMode :: Recursive ) ?;
901+
902+ fs:: write ( dir. path ( ) . join ( "file.txt" ) , b"Lorem ipsum" ) ?;
903+
904+ let events = rx
905+ . recv_timeout ( Duration :: from_secs ( 10 ) )
906+ . expect ( "no events received" )
907+ . expect ( "received an error" ) ;
908+
909+ assert ! ( !events. is_empty( ) , "received empty event list" ) ;
910+
911+ Ok ( ( ) )
912+ }
890913}
0 commit comments