11use file_owner_finder:: FileOwnerFinder ;
22use mapper:: { OwnerMatcher , Source , TeamName } ;
33use std:: {
4+ error:: Error ,
45 fmt:: { self , Display } ,
56 path:: Path ,
67 sync:: Arc ,
@@ -38,6 +39,15 @@ pub struct TeamOwnership {
3839 pub globs : Vec < String > ,
3940}
4041
42+ impl TeamOwnership {
43+ fn new ( heading : String ) -> Self {
44+ Self {
45+ heading,
46+ ..Default :: default ( )
47+ }
48+ }
49+ }
50+
4151impl Display for FileOwner {
4252 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
4353 let sources = self
@@ -128,7 +138,7 @@ impl Ownership {
128138 }
129139
130140 #[ instrument( level = "debug" , skip_all) ]
131- pub fn for_team ( & self , team_name : & str ) -> Result < Vec < TeamOwnership > , Box < dyn std :: error :: Error > > {
141+ pub fn for_team ( & self , team_name : & str ) -> Result < Vec < TeamOwnership > , Box < dyn Error > > {
132142 info ! ( "getting team ownership for {}" , team_name) ;
133143 let team = self . project . get_team ( team_name) . ok_or ( "Team not found" ) ?;
134144
@@ -155,33 +165,37 @@ impl Ownership {
155165 }
156166}
157167
158- fn parse_for_team ( team_name : String , codeowners_file : & str ) -> Result < Vec < TeamOwnership > , Box < dyn std :: error :: Error > > {
168+ fn parse_for_team ( team_name : String , codeowners_file : & str ) -> Result < Vec < TeamOwnership > , Box < dyn Error > > {
159169 let mut output = vec ! [ ] ;
160- let mut current_section = TeamOwnership :: default ( ) ;
170+ let mut current_section: Option < TeamOwnership > = None ;
161171 let input: String = codeowners_file. replace ( & FileGenerator :: disclaimer ( ) . join ( "\n " ) , "" ) ;
162172
163173 for line in input. trim_start ( ) . lines ( ) {
164174 match line {
165175 comment if comment. starts_with ( "#" ) => {
166- current_section. heading = comment. to_string ( ) ;
176+ current_section = Some ( TeamOwnership :: new ( comment. to_string ( ) ) ) ;
167177 }
168178 "" => {
169- if current_section != TeamOwnership :: default ( ) {
170- output. push ( current_section . clone ( ) ) ;
171- current_section = TeamOwnership :: default ( ) ;
179+ if let Some ( cs ) = current_section {
180+ output. push ( cs . clone ( ) ) ;
181+ current_section = None ;
172182 }
173183 }
174- team_line if team_line. ends_with ( & team_name) => {
175- let v: Vec < & str > = team_line. split ( ' ' ) . collect ( ) ;
176- let s = v. first ( ) . ok_or ( "malformed team line" ) ?;
184+ team_line if team_line. ends_with ( & team_name) => match current_section {
185+ Some ( ref mut cs) => {
186+ let v: Vec < & str > = team_line. split ( ' ' ) . collect ( ) ;
187+ let s = v. first ( ) . ok_or ( "Malformed team line" ) ?;
177188
178- current_section. globs . push ( s. to_string ( ) ) ;
179- }
189+ cs. globs . push ( s. to_string ( ) ) ;
190+ }
191+ _ => return Err ( Box :: from ( "Missing preceding section header" ) ) ,
192+ } ,
180193 _ => { }
181194 }
182195 }
183- if current_section != TeamOwnership :: default ( ) {
184- output. push ( current_section. clone ( ) ) ;
196+
197+ if let Some ( cs) = current_section {
198+ output. push ( cs. clone ( ) ) ;
185199 }
186200
187201 Ok ( output)
@@ -193,7 +207,7 @@ mod tests {
193207 use crate :: common_test:: tests:: { build_ownership_with_all_mappers, vecs_match} ;
194208
195209 #[ test]
196- fn test_for_file_owner ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
210+ fn test_for_file_owner ( ) -> Result < ( ) , Box < dyn Error > > {
197211 let ownership = build_ownership_with_all_mappers ( ) ?;
198212 let file_owners = ownership. for_file ( "app/consumers/directory_owned.rb" ) . unwrap ( ) ;
199213 assert_eq ! ( file_owners. len( ) , 1 ) ;
@@ -203,31 +217,31 @@ mod tests {
203217 }
204218
205219 #[ test]
206- fn test_for_file_no_owner ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
220+ fn test_for_file_no_owner ( ) -> Result < ( ) , Box < dyn Error > > {
207221 let ownership = build_ownership_with_all_mappers ( ) ?;
208222 let file_owners = ownership. for_file ( "app/madeup/foo.rb" ) . unwrap ( ) ;
209223 assert_eq ! ( file_owners. len( ) , 0 ) ;
210224 Ok ( ( ) )
211225 }
212226
213227 #[ test]
214- fn test_for_team_not_found ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
228+ fn test_for_team ( ) -> Result < ( ) , Box < dyn Error > > {
215229 let ownership = build_ownership_with_all_mappers ( ) ?;
216- let team_ownership = ownership. for_team ( "Nope " ) ;
217- assert ! ( team_ownership. is_err ( ) , "Team not found" ) ;
230+ let team_ownership = ownership. for_team ( "Bar " ) ;
231+ assert ! ( team_ownership. is_ok ( ) ) ;
218232 Ok ( ( ) )
219233 }
220234
221235 #[ test]
222- fn test_for_team ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
236+ fn test_for_team_not_found ( ) -> Result < ( ) , Box < dyn Error > > {
223237 let ownership = build_ownership_with_all_mappers ( ) ?;
224- let team_ownership = ownership. for_team ( "Bar " ) ;
225- assert ! ( team_ownership. is_ok ( ) ) ;
238+ let team_ownership = ownership. for_team ( "Nope " ) ;
239+ assert ! ( team_ownership. is_err ( ) , "Team not found" ) ;
226240 Ok ( ( ) )
227241 }
228242
229243 #[ test]
230- fn test_parse_for_team_trims_header ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
244+ fn test_parse_for_team_trims_header ( ) -> Result < ( ) , Box < dyn Error > > {
231245 let codeownership_file = r#"
232246# STOP! - DO NOT EDIT THIS FILE MANUALLY
233247# This file was automatically generated by "bin/codeownership validate".
@@ -240,29 +254,29 @@ mod tests {
240254
241255"# ;
242256
243- let team_ownership = parse_for_team ( "@Bar" . to_string ( ) , & codeownership_file) ?;
257+ let team_ownership = parse_for_team ( "@Bar" . to_string ( ) , codeownership_file) ?;
244258 assert ! ( team_ownership. is_empty( ) ) ;
245259 Ok ( ( ) )
246260 }
247261
248262 #[ test]
249- fn test_parse_for_team_includes_owned_globs ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
263+ fn test_parse_for_team_includes_owned_globs ( ) -> Result < ( ) , Box < dyn Error > > {
250264 let codeownership_file = r#"
251265# First Section
252- /path/to/owned/**/* @Foo
253- /path/to/not/owned/**/* @Bar
266+ /path/to/owned @Foo
267+ /path/to/not/owned @Bar
254268
255269# Last Section
256270/another/owned/path @Foo
257271"# ;
258272
259- let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , & codeownership_file) ?;
273+ let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , codeownership_file) ?;
260274 vecs_match (
261275 & team_ownership,
262276 & vec ! [
263277 TeamOwnership {
264278 heading: "# First Section" . to_string( ) ,
265- globs: vec![ "/path/to/owned/**/* " . to_string( ) ] ,
279+ globs: vec![ "/path/to/owned" . to_string( ) ] ,
266280 } ,
267281 TeamOwnership {
268282 heading: "# Last Section" . to_string( ) ,
@@ -274,14 +288,14 @@ mod tests {
274288 }
275289
276290 #[ test]
277- fn test_parse_for_team_with_partial_team_match ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
291+ fn test_parse_for_team_with_partial_team_match ( ) -> Result < ( ) , Box < dyn Error > > {
278292 let codeownership_file = r#"
279293# First Section
280294/path/to/owned @Foo
281295/path/to/not/owned @FooBar
282296"# ;
283297
284- let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , & codeownership_file) ?;
298+ let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , codeownership_file) ?;
285299 vecs_match (
286300 & team_ownership,
287301 & vec ! [ TeamOwnership {
@@ -293,7 +307,7 @@ mod tests {
293307 }
294308
295309 #[ test]
296- fn test_parse_for_team_with_trailing_newlines ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
310+ fn test_parse_for_team_with_trailing_newlines ( ) -> Result < ( ) , Box < dyn Error > > {
297311 let codeownership_file = r#"
298312# First Section
299313/path/to/owned @Foo
@@ -305,7 +319,7 @@ mod tests {
305319
306320"# ;
307321
308- let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , & codeownership_file) ?;
322+ let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , codeownership_file) ?;
309323 vecs_match (
310324 & team_ownership,
311325 & vec ! [
@@ -323,12 +337,12 @@ mod tests {
323337 }
324338
325339 #[ test]
326- fn test_parse_for_team_without_trailing_newline ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
340+ fn test_parse_for_team_without_trailing_newline ( ) -> Result < ( ) , Box < dyn Error > > {
327341 let codeownership_file = r#"
328342# First Section
329343/path/to/owned @Foo"# ;
330344
331- let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , & codeownership_file) ?;
345+ let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , codeownership_file) ?;
332346 vecs_match (
333347 & team_ownership,
334348 & vec ! [ TeamOwnership {
@@ -338,4 +352,19 @@ mod tests {
338352 ) ;
339353 Ok ( ( ) )
340354 }
355+
356+ #[ test]
357+ fn test_parse_for_team_with_missing_section_header ( ) -> Result < ( ) , Box < dyn Error > > {
358+ let codeownership_file = r#"
359+ # First Section
360+ /path/to/owned @Foo
361+
362+ /another/owned/path @Foo
363+ "# ;
364+
365+ let team_ownership = parse_for_team ( "@Foo" . to_string ( ) , codeownership_file) ;
366+ dbg ! ( & team_ownership) ;
367+ assert ! ( team_ownership. is_err( ) ) ;
368+ Ok ( ( ) )
369+ }
341370}
0 commit comments