1- use crate :: { error:: Error , Res } ;
2- use ignore:: gitignore:: GitignoreBuilder ;
3- use notify:: { Event , EventKind , RecursiveMode , Watcher } ;
1+ use crate :: { error:: Error , open_repo , Res } ;
2+ use ignore:: gitignore:: { gitconfig_excludes_path , GitignoreBuilder , Gitignore } ;
3+ use notify:: { Event , EventKind , RecommendedWatcher , RecursiveMode , Watcher } ;
44use std:: {
55 path:: Path ,
6+ path:: PathBuf ,
67 sync:: {
78 atomic:: { AtomicBool , Ordering } ,
89 Arc ,
910 } ,
10- thread,
1111} ;
1212
1313pub struct FileWatcher {
14+ _watcher : RecommendedWatcher ,
1415 pending_updates : Arc < AtomicBool > ,
1516}
1617
1718impl FileWatcher {
18- pub fn new ( path : & Path ) -> Res < Self > {
19+ pub fn new ( repo_dir : & Path ) -> Res < Self > {
1920 let pending_updates = Arc :: new ( AtomicBool :: new ( false ) ) ;
2021 let pending_updates_w = pending_updates. clone ( ) ;
2122
22- let gitignore = GitignoreBuilder :: new ( path)
23- . add_line ( None , super :: LOG_FILE_NAME )
24- . map_err ( Error :: FileWatcherGitignore ) ?
25- . build ( )
26- . unwrap ( ) ;
23+ let path_buf = repo_dir. to_owned ( ) ;
24+ let path_buf_copy = path_buf. clone ( ) ;
25+ let mut gitignore = build_gitignore ( repo_dir) ?;
2726
2827 let mut watcher = notify:: recommended_watcher ( move |res : Result < Event , notify:: Error > | {
2928 if let Ok ( event) = res {
@@ -32,7 +31,20 @@ impl FileWatcher {
3231 }
3332
3433 for path in event. paths {
35- if !gitignore. matched ( & path, path. is_dir ( ) ) . is_ignore ( ) {
34+ if path
35+ . file_name ( )
36+ . map_or ( false , |name| name == ".gitignore" )
37+ {
38+ log:: info!( "Rebuilding gitignore ruleset" ) ;
39+ if let Ok ( new_gitignore) = build_gitignore ( & path_buf_copy) {
40+ gitignore = new_gitignore;
41+ }
42+ }
43+
44+ if !gitignore
45+ . matched_path_or_any_parents ( & path, path. is_dir ( ) )
46+ . is_ignore ( )
47+ {
3648 log:: info!( "File changed: {:?}" , path) ;
3749 pending_updates_w. store ( true , Ordering :: Relaxed ) ;
3850 break ;
@@ -42,17 +54,18 @@ impl FileWatcher {
4254 } )
4355 . map_err ( Error :: FileWatcher ) ?;
4456
45- let path_buf = path. to_owned ( ) ;
46- thread:: spawn ( move || {
47- if let Err ( err) = watcher
48- . watch ( path_buf. as_ref ( ) , RecursiveMode :: Recursive )
49- . map_err ( Error :: FileWatcher )
50- {
51- log:: error!( "Couldn't start file-watcher due to: {}" , err) ;
52- }
53- } ) ;
57+ watcher
58+ . watch ( & path_buf, RecursiveMode :: Recursive )
59+ . map_err ( Error :: FileWatcher ) ?;
60+ log:: info!(
61+ "File watcher started (kind: {:?})" ,
62+ RecommendedWatcher :: kind( )
63+ ) ;
5464
55- Ok ( Self { pending_updates } )
65+ Ok ( Self {
66+ _watcher : watcher,
67+ pending_updates,
68+ } )
5669 }
5770
5871 pub fn pending_updates ( & self ) -> bool {
@@ -66,3 +79,32 @@ fn is_changed(event: &Event) -> bool {
6679 EventKind :: Create ( _) | EventKind :: Modify ( _) | EventKind :: Remove ( _)
6780 )
6881}
82+
83+ fn build_gitignore ( path : & Path ) -> Res < Gitignore > {
84+ let mut gitignore_builder = GitignoreBuilder :: new ( path) ;
85+ for gitignore_path in repo_gitignore_paths ( path) ? {
86+ gitignore_builder. add ( gitignore_path) ;
87+ }
88+ gitignore_builder. add_line ( None , super :: LOG_FILE_NAME ) . ok ( ) ;
89+ gitignore_builder
90+ . build ( )
91+ . map_err ( Error :: FileWatcherGitignore )
92+ }
93+
94+ fn repo_gitignore_paths ( repo_dir : & Path ) -> Res < Vec < PathBuf > > {
95+ let mut gitignore_paths = gitconfig_excludes_path ( ) . map_or_else ( || vec ! [ ] , |path| vec ! [ path] ) ;
96+ gitignore_paths
97+ . extend (
98+ open_repo ( repo_dir) ?
99+ . index ( )
100+ . map_err ( Error :: OpenRepo ) ?
101+ . iter ( )
102+ . filter_map ( |entry| {
103+ match std:: str:: from_utf8 ( & entry. path ) . map ( Path :: new) {
104+ Ok ( path) if path. file_name ( ) == Some ( std:: ffi:: OsStr :: new ( ".gitignore" ) ) => Some ( path. to_path_buf ( ) ) ,
105+ _ => None
106+ }
107+ } )
108+ ) ;
109+ Ok ( gitignore_paths)
110+ }
0 commit comments