@@ -5,6 +5,7 @@ use crate::{
55use fast_glob:: glob_match;
66use memoize:: memoize;
77use regex:: Regex ;
8+ use rayon:: prelude:: * ;
89use std:: {
910 collections:: HashMap ,
1011 error:: Error ,
@@ -23,32 +24,69 @@ pub struct Parser {
2324
2425impl Parser {
2526 pub fn teams_from_files_paths ( & self , file_paths : & [ PathBuf ] ) -> Result < HashMap < String , Team > , Box < dyn Error > > {
26- todo ! ( )
27- }
28-
29- pub fn team_from_file_path ( & self , file_path : & Path ) -> Result < Option < Team > , Box < dyn Error > > {
30- let file_path_str = file_path
31- . to_str ( )
32- . ok_or ( IoError :: new ( std:: io:: ErrorKind :: InvalidInput , "Invalid file path" ) ) ?;
33- let slash_prefixed = if file_path_str. starts_with ( "/" ) {
34- file_path_str. to_string ( )
35- } else {
36- format ! ( "/{}" , file_path_str)
37- } ;
38-
39- let codeowners_lines_in_priorty = build_codeowners_lines_in_priority ( self . codeowners_file_path . to_string_lossy ( ) . into_owned ( ) ) ;
40- for line in codeowners_lines_in_priorty {
41- let ( glob, team_name) = line
42- . split_once ( ' ' )
43- . ok_or ( IoError :: new ( std:: io:: ErrorKind :: InvalidInput , "Invalid line" ) ) ?;
44- if glob_match ( glob, & slash_prefixed) {
45- let tbn = teams_by_github_team_name ( self . absolute_team_files_globs ( ) ) ;
46- let team: Option < Team > = tbn. get ( team_name. to_string ( ) . as_str ( ) ) . cloned ( ) ;
47- return Ok ( team) ;
48- }
27+ let mut file_inputs: Vec < ( String , String ) > = Vec :: with_capacity ( file_paths. len ( ) ) ;
28+ for path in file_paths {
29+ let file_path_str = path
30+ . to_str ( )
31+ . ok_or ( IoError :: new ( std:: io:: ErrorKind :: InvalidInput , "Invalid file path" ) ) ?;
32+ let key = file_path_str. to_string ( ) ;
33+ let slash_prefixed = if file_path_str. starts_with ( '/' ) {
34+ file_path_str. to_string ( )
35+ } else {
36+ format ! ( "/{}" , file_path_str)
37+ } ;
38+ file_inputs. push ( ( key, slash_prefixed) ) ;
39+ }
40+
41+ if file_inputs. is_empty ( ) {
42+ return Ok ( HashMap :: new ( ) ) ;
43+ }
44+
45+ let codeowners_lines_in_priority = build_codeowners_lines_in_priority (
46+ self . codeowners_file_path . to_string_lossy ( ) . into_owned ( ) ,
47+ ) ;
48+ // Pre-parse lines once to avoid repeated split and to handle malformed lines early
49+ let codeowners_entries: Vec < ( String , String ) > = codeowners_lines_in_priority
50+ . iter ( )
51+ . map ( |line| {
52+ line
53+ . split_once ( ' ' )
54+ . map ( |( glob, team_name) | ( glob. to_string ( ) , team_name. to_string ( ) ) )
55+ . ok_or_else ( || IoError :: new ( std:: io:: ErrorKind :: InvalidInput , "Invalid line" ) )
56+ } )
57+ . collect :: < Result < _ , IoError > > ( )
58+ . map_err ( |e| Box :: new ( e) as Box < dyn Error > ) ?;
59+ let teams_by_name = teams_by_github_team_name ( self . absolute_team_files_globs ( ) ) ;
60+
61+ // Parallelize across files: for each file, scan lines in priority order
62+ let result_pairs: Vec < ( String , Team ) > = file_inputs
63+ . par_iter ( )
64+ . filter_map ( |( key, prefixed) | {
65+ for ( glob, team_name) in & codeowners_entries {
66+ if glob_match ( glob, prefixed) {
67+ // Stop at first match (highest priority). If team missing, treat as unowned.
68+ if let Some ( team) = teams_by_name. get ( team_name) {
69+ return Some ( ( key. clone ( ) , team. clone ( ) ) ) ;
70+ } else {
71+ return None ;
72+ }
73+ }
74+ }
75+ None
76+ } )
77+ . collect ( ) ;
78+
79+ let mut result: HashMap < String , Team > = HashMap :: with_capacity ( result_pairs. len ( ) ) ;
80+ for ( k, t) in result_pairs {
81+ result. insert ( k, t) ;
4982 }
5083
51- Ok ( None )
84+ Ok ( result)
85+ }
86+
87+ pub fn team_from_file_path ( & self , file_path : & Path ) -> Result < Option < Team > , Box < dyn Error > > {
88+ let teams = self . teams_from_files_paths ( & [ file_path. to_path_buf ( ) ] ) ?;
89+ Ok ( teams. get ( file_path. to_string_lossy ( ) . into_owned ( ) . as_str ( ) ) . cloned ( ) )
5290 }
5391
5492 fn absolute_team_files_globs ( & self ) -> Vec < String > {
0 commit comments