5757//! - `crossbeam` enabled by default, adds [`DebounceEventHandler`](DebounceEventHandler) support for crossbeam channels.
5858//! Also enables crossbeam-channel in the re-exported notify. You may want to disable this when using the tokio async runtime.
5959//! - `serde` enables serde support for events.
60- //!
60+ //!
6161//! # Caveats
62- //!
62+ //!
6363//! As all file events are sourced from notify, the [known problems](https://docs.rs/notify/latest/notify/#known-problems) section applies here too.
6464
6565mod cache;
@@ -69,7 +69,8 @@ mod debounced_event;
6969mod testing;
7070
7171use std:: {
72- collections:: { HashMap , VecDeque } ,
72+ cmp:: Reverse ,
73+ collections:: { BinaryHeap , HashMap , VecDeque } ,
7374 path:: PathBuf ,
7475 sync:: {
7576 atomic:: { AtomicBool , Ordering } ,
@@ -249,17 +250,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
249250
250251 self . queues = queues_remaining;
251252
252- // order events for different files chronologically, but keep the order of events for the same file
253- events_expired. sort_by ( |event_a, event_b| {
254- // use the last path because rename events are emitted for the target path
255- if event_a. paths . last ( ) == event_b. paths . last ( ) {
256- std:: cmp:: Ordering :: Equal
257- } else {
258- event_a. time . cmp ( & event_b. time )
259- }
260- } ) ;
261-
262- events_expired
253+ sort_events ( events_expired)
263254 }
264255
265256 /// Returns all currently stored errors
@@ -654,6 +645,49 @@ pub fn new_debouncer<F: DebounceEventHandler>(
654645 )
655646}
656647
648+ fn sort_events ( events : Vec < DebouncedEvent > ) -> Vec < DebouncedEvent > {
649+ let mut sorted = Vec :: with_capacity ( events. len ( ) ) ;
650+
651+ // group events by path
652+ let mut events_by_path: HashMap < _ , VecDeque < _ > > =
653+ events. into_iter ( ) . fold ( HashMap :: new ( ) , |mut acc, event| {
654+ acc. entry ( event. paths . last ( ) . cloned ( ) . unwrap_or_default ( ) )
655+ . or_default ( )
656+ . push_back ( event) ;
657+ acc
658+ } ) ;
659+
660+ // push events for different paths in chronological order and keep the order of events with the same path
661+
662+ let mut min_time_heap = events_by_path
663+ . iter ( )
664+ . map ( |( path, events) | Reverse ( ( events[ 0 ] . time , path. clone ( ) ) ) )
665+ . collect :: < BinaryHeap < _ > > ( ) ;
666+
667+ while let Some ( Reverse ( ( min_time, path) ) ) = min_time_heap. pop ( ) {
668+ // unwrap is safe because only paths from `events_by_path` are added to `min_time_heap`
669+ // and they are never removed from `events_by_path`.
670+ let events = events_by_path. get_mut ( & path) . unwrap ( ) ;
671+
672+ let mut push_next = false ;
673+
674+ while events. front ( ) . map_or ( false , |event| event. time <= min_time) {
675+ // unwrap is safe beause `pop_front` mus return some in order to enter the loop
676+ let event = events. pop_front ( ) . unwrap ( ) ;
677+ sorted. push ( event) ;
678+ push_next = true ;
679+ }
680+
681+ if push_next {
682+ if let Some ( event) = events. front ( ) {
683+ min_time_heap. push ( Reverse ( ( event. time , path) ) ) ;
684+ }
685+ }
686+ }
687+
688+ sorted
689+ }
690+
657691#[ cfg( test) ]
658692mod tests {
659693 use std:: { fs, path:: Path } ;
@@ -702,7 +736,9 @@ mod tests {
702736 "emit_close_events_only_once" ,
703737 "emit_modify_event_after_close_event" ,
704738 "emit_needs_rescan_event" ,
705- "read_file_id_without_create_event"
739+ "read_file_id_without_create_event" ,
740+ "sort_events_chronologically" ,
741+ "sort_events_with_reordering"
706742 ) ]
707743 file_name : & str ,
708744 ) {
0 commit comments