@@ -67,19 +67,56 @@ impl Source {
6767#[ derive( Debug , PartialEq ) ]
6868pub enum OwnerMatcher {
6969 ExactMatches ( HashMap < PathBuf , TeamName > , Source ) ,
70- Glob { glob : String , team_name : TeamName , source : Source } ,
70+ Glob {
71+ glob : String ,
72+ subtracted_globs : Vec < String > ,
73+ team_name : TeamName ,
74+ source : Source ,
75+ } ,
7176}
7277
7378impl OwnerMatcher {
79+ pub fn new_glob_with_candidate_subtracted_globs (
80+ glob : String ,
81+ candidate_subtracted_globs : & [ String ] ,
82+ team_name : TeamName ,
83+ source : Source ,
84+ ) -> Self {
85+ let subtracted_globs = candidate_subtracted_globs
86+ . iter ( )
87+ . filter ( |candidate_subtracted_glob| {
88+ glob_match ( candidate_subtracted_glob, & glob) || glob_match ( & glob, candidate_subtracted_glob)
89+ } )
90+ . cloned ( )
91+ . collect ( ) ;
92+ OwnerMatcher :: Glob {
93+ glob,
94+ subtracted_globs,
95+ team_name,
96+ source,
97+ }
98+ }
99+
100+ pub fn new_glob ( glob : String , team_name : TeamName , source : Source ) -> Self {
101+ OwnerMatcher :: Glob {
102+ glob,
103+ subtracted_globs : vec ! [ ] ,
104+ team_name,
105+ source,
106+ }
107+ }
108+
74109 pub fn owner_for ( & self , relative_path : & Path ) -> ( Option < & TeamName > , & Source ) {
75110 match self {
76- OwnerMatcher :: Glob { glob, team_name, source } => {
77- if glob_match ( glob, relative_path. to_str ( ) . unwrap ( ) ) {
78- ( Some ( team_name) , source)
79- } else {
80- ( None , source)
81- }
82- }
111+ OwnerMatcher :: Glob {
112+ glob,
113+ subtracted_globs,
114+ team_name,
115+ source,
116+ } => relative_path
117+ . to_str ( )
118+ . filter ( |path| glob_match ( glob, path) && !subtracted_globs. iter ( ) . any ( |subtracted| glob_match ( subtracted, path) ) )
119+ . map_or ( ( None , source) , |_| ( Some ( team_name) , source) ) ,
83120 OwnerMatcher :: ExactMatches ( path_to_team, source) => ( path_to_team. get ( relative_path) , source) ,
84121 }
85122 }
@@ -89,14 +126,15 @@ impl OwnerMatcher {
89126mod tests {
90127 use super :: * ;
91128
92- fn assert_owner_for ( glob : & str , relative_path : & str , expect_match : bool ) {
129+ fn assert_owner_for ( glob : & str , subtracted_globs : & [ & str ] , relative_path : & str , expect_match : bool ) {
93130 let source = Source :: Directory ( "packs/bam" . to_string ( ) ) ;
94131 let team_name = "team1" . to_string ( ) ;
95- let owner_matcher = OwnerMatcher :: Glob {
96- glob : glob. to_string ( ) ,
97- team_name : team_name. clone ( ) ,
98- source : source. clone ( ) ,
99- } ;
132+ let owner_matcher = OwnerMatcher :: new_glob_with_candidate_subtracted_globs (
133+ glob. to_string ( ) ,
134+ & subtracted_globs. iter ( ) . map ( |s| s. to_string ( ) ) . collect :: < Vec < String > > ( ) ,
135+ team_name. clone ( ) ,
136+ source. clone ( ) ,
137+ ) ;
100138 let response = owner_matcher. owner_for ( Path :: new ( relative_path) ) ;
101139 if expect_match {
102140 assert_eq ! ( response, ( Some ( & team_name) , & source) ) ;
@@ -107,26 +145,50 @@ mod tests {
107145
108146 #[ test]
109147 fn owner_for_without_brackets_in_glob ( ) {
110- assert_owner_for ( "packs/bam/**/**" , "packs/bam/app/components/sidebar.jsx" , true ) ;
111- assert_owner_for ( "packs/bam/**/**" , "packs/baz/app/components/sidebar.jsx" , false ) ;
112- assert_owner_for ( "packs/bam/**/**" , "packs/bam/app/[components]/gadgets/sidebar.jsx" , true ) ;
113- assert_owner_for ( "packs/bam/**/**" , "packs/bam/app/sidebar_[component].jsx" , true ) ;
148+ assert_owner_for ( "packs/bam/**/**" , & [ ] , "packs/bam/app/components/sidebar.jsx" , true ) ;
149+ assert_owner_for ( "packs/bam/**/**" , & [ ] , "packs/baz/app/components/sidebar.jsx" , false ) ;
150+ assert_owner_for ( "packs/bam/**/**" , & [ ] , "packs/bam/app/[components]/gadgets/sidebar.jsx" , true ) ;
151+ assert_owner_for ( "packs/bam/**/**" , & [ ] , "packs/bam/app/sidebar_[component].jsx" , true ) ;
152+ assert_owner_for (
153+ "packs/bam/**/**" ,
154+ & [ "packs/bam/app/excluded/**" ] ,
155+ "packs/bam/app/excluded/sidebar_[component].jsx" ,
156+ false ,
157+ ) ;
158+ }
159+
160+ #[ test]
161+ fn subtracted_globs ( ) {
162+ assert_owner_for (
163+ "packs/bam/**/**" ,
164+ & [ "packs/bam/app/excluded/**" ] ,
165+ "packs/bam/app/excluded/some_file.rb" ,
166+ false ,
167+ ) ;
168+ assert_owner_for (
169+ "packs/bam/**/**" ,
170+ & [ "packs/bam/app/excluded/**" ] ,
171+ "packs/bam/app/not_excluded/some_file.rb" ,
172+ true ,
173+ ) ;
114174 }
115175
116176 #[ test]
117177 fn owner_for_with_brackets_in_glob ( ) {
118178 assert_owner_for (
119179 "packs/bam/app/\\ [components\\ ]/**/**" ,
180+ & [ ] ,
120181 "packs/bam/app/[components]/gadgets/sidebar.jsx" ,
121182 true ,
122183 ) ;
123- assert_owner_for ( "packs/\\ [bam\\ ]/**/**" , "packs/[bam]/app/components/sidebar.jsx" , true ) ;
184+ assert_owner_for ( "packs/\\ [bam\\ ]/**/**" , & [ ] , "packs/[bam]/app/components/sidebar.jsx" , true ) ;
124185 }
125186
126187 #[ test]
127188 fn owner_for_with_multiple_brackets_in_glob ( ) {
128189 assert_owner_for (
129190 "packs/\\ [bam\\ ]/bar/\\ [foo\\ ]/**/**" ,
191+ & [ ] ,
130192 "packs/[bam]/bar/[foo]/app/components/sidebar.jsx" ,
131193 true ,
132194 ) ;
@@ -150,4 +212,38 @@ mod tests {
150212 ) ;
151213 assert_eq ! ( Source :: TeamYml . to_string( ) , "Teams own their configuration files" ) ;
152214 }
215+
216+ #[ test]
217+ fn test_new_glob_with_candidate_subtracted_globs ( ) {
218+ assert_new_glob_with_candidate_subtracted_globs ( "packs/bam/**/**" , & [ ] , & [ ] ) ;
219+ assert_new_glob_with_candidate_subtracted_globs ( "packs/bam/**/**" , & [ "packs/bam/app/**/**" ] , & [ "packs/bam/app/**/**" ] ) ;
220+ assert_new_glob_with_candidate_subtracted_globs (
221+ "packs/bam/**/**" ,
222+ & [ "packs/bam/app/an/exceptional/path/it.rb" ] ,
223+ & [ "packs/bam/app/an/exceptional/path/it.rb" ] ,
224+ ) ;
225+ assert_new_glob_with_candidate_subtracted_globs ( "packs/bam/**/**" , & [ "packs/bam.rb" ] , & [ ] ) ;
226+ assert_new_glob_with_candidate_subtracted_globs ( "packs/bam/**/**" , & [ "packs/nope/app/**/**" ] , & [ ] ) ;
227+ assert_new_glob_with_candidate_subtracted_globs ( "packs/**" , & [ "packs/yep/app/**/**" ] , & [ "packs/yep/app/**/**" ] ) ;
228+ assert_new_glob_with_candidate_subtracted_globs ( "packs/foo.yml" , & [ "packs/foo/**/**" ] , & [ ] ) ;
229+ }
230+
231+ fn assert_new_glob_with_candidate_subtracted_globs (
232+ glob : & str ,
233+ candidate_subtracted_globs : & [ & str ] ,
234+ expected_subtracted_globs : & [ & str ] ,
235+ ) {
236+ let owner_matcher = OwnerMatcher :: new_glob_with_candidate_subtracted_globs (
237+ glob. to_string ( ) ,
238+ & candidate_subtracted_globs. iter ( ) . map ( |s| s. to_string ( ) ) . collect :: < Vec < String > > ( ) ,
239+ "team1" . to_string ( ) ,
240+ Source :: TeamGlob ( glob. to_string ( ) ) ,
241+ ) ;
242+
243+ if let OwnerMatcher :: Glob { subtracted_globs, .. } = owner_matcher {
244+ assert_eq ! ( subtracted_globs, expected_subtracted_globs) ;
245+ } else {
246+ panic ! ( "Expected a Glob matcher" ) ;
247+ }
248+ }
153249}
0 commit comments