@@ -6,10 +6,8 @@ use std::{
66
77use fast_glob:: glob_match;
88use glob:: glob;
9- use lazy_static:: lazy_static;
10- use regex:: Regex ;
119
12- use crate :: { config:: Config , project:: Team } ;
10+ use crate :: { config:: Config , project:: Team , project_file_builder :: build_project_file_without_cache } ;
1311
1412use super :: { FileOwner , mapper:: Source } ;
1513
@@ -106,7 +104,7 @@ pub fn find_file_owners(project_root: &Path, config: &Config, file_path: &Path)
106104}
107105
108106fn build_teams_by_name_map ( teams : & [ Team ] ) -> HashMap < String , Team > {
109- let mut map = HashMap :: new ( ) ;
107+ let mut map = HashMap :: with_capacity ( teams . len ( ) * 2 ) ;
110108 for team in teams {
111109 map. insert ( team. name . clone ( ) , team. clone ( ) ) ;
112110 map. insert ( team. github_team . clone ( ) , team. clone ( ) ) ;
@@ -117,7 +115,7 @@ fn build_teams_by_name_map(teams: &[Team]) -> HashMap<String, Team> {
117115fn load_teams ( project_root : & Path , team_file_globs : & [ String ] ) -> std:: result:: Result < Vec < Team > , String > {
118116 let mut teams: Vec < Team > = Vec :: new ( ) ;
119117 for glob_str in team_file_globs {
120- let absolute_glob = format ! ( "{}/{}" , project_root. display ( ) , glob_str) ;
118+ let absolute_glob = project_root. join ( glob_str) . to_string_lossy ( ) . into_owned ( ) ;
121119 let paths = glob ( & absolute_glob) . map_err ( |e| e. to_string ( ) ) ?;
122120 for path in paths. flatten ( ) {
123121 match Team :: from_team_file_path ( path. clone ( ) ) {
@@ -132,30 +130,14 @@ fn load_teams(project_root: &Path, team_file_globs: &[String]) -> std::result::R
132130 Ok ( teams)
133131}
134132
135- lazy_static ! {
136- // Allow optional leading whitespace before the comment marker
137- static ref TOP_OF_FILE_TEAM_AT_REGEX : Option <Regex > = Regex :: new( r#"^\s*(?:#|//)\s*@team\s+(.+)$"# ) . ok( ) ;
138- static ref TOP_OF_FILE_TEAM_COLON_REGEX : Option <Regex > = Regex :: new( r#"(?i)^\s*(?:#|//)\s*team\s*:\s*(.+)$"# ) . ok( ) ;
139- }
133+ // no regex: parse cheaply with ASCII-aware checks
140134
141135fn read_top_of_file_team ( path : & Path ) -> Option < String > {
142- let content = fs:: read_to_string ( path) . ok ( ) ?;
143- let line = content. lines ( ) . next ( ) ?;
144-
145- if let Some ( re) = & * TOP_OF_FILE_TEAM_AT_REGEX {
146- if let Some ( cap) = re. captures ( line) {
147- if let Some ( m) = cap. get ( 1 ) {
148- return Some ( m. as_str ( ) . to_string ( ) ) ;
149- }
150- }
151- }
152- if let Some ( re) = & * TOP_OF_FILE_TEAM_COLON_REGEX {
153- if let Some ( cap) = re. captures ( line) {
154- if let Some ( m) = cap. get ( 1 ) {
155- return Some ( m. as_str ( ) . to_string ( ) ) ;
156- }
157- }
136+ let project_file = build_project_file_without_cache ( & path. to_path_buf ( ) ) ;
137+ if let Some ( owner) = project_file. owner {
138+ return Some ( owner) ;
158139 }
140+
159141 None
160142}
161143
@@ -167,34 +149,32 @@ fn most_specific_directory_owner(
167149 let mut current = project_root. join ( relative_file_path) ;
168150 let mut best: Option < ( String , Source ) > = None ;
169151 loop {
170- let parent_opt = current. parent ( ) . map ( |p| p. to_path_buf ( ) ) ;
171- let Some ( parent) = parent_opt else { break } ;
172- let codeowner_path = parent. join ( ".codeowner" ) ;
152+ if !current. pop ( ) {
153+ break ;
154+ }
155+ let codeowner_path = current. join ( ".codeowner" ) ;
173156 if let Ok ( owner_str) = fs:: read_to_string ( & codeowner_path) {
174157 let owner = owner_str. trim ( ) ;
175158 if let Some ( team) = teams_by_name. get ( owner) {
176- let relative_dir = parent
159+ let relative_dir = current
177160 . strip_prefix ( project_root)
178- . unwrap_or ( parent . as_path ( ) )
161+ . unwrap_or ( current . as_path ( ) )
179162 . to_string_lossy ( )
180163 . to_string ( ) ;
181164 let candidate = ( team. name . clone ( ) , Source :: Directory ( relative_dir) ) ;
182165 match & best {
183166 None => best = Some ( candidate) ,
184167 Some ( ( _, existing_source) ) => {
185- let existing_len = source_directory_depth ( existing_source) ;
186- let candidate_len = source_directory_depth ( & candidate. 1 ) ;
187- if candidate_len > existing_len {
168+ if candidate. 1 . len ( ) > existing_source. len ( ) {
188169 best = Some ( candidate) ;
189170 }
190171 }
191172 }
192173 }
193174 }
194- if parent == project_root {
175+ if current == project_root {
195176 break ;
196177 }
197- current = parent. clone ( ) ;
198178 }
199179 best
200180}
@@ -207,17 +187,18 @@ fn nearest_package_owner(
207187) -> Option < ( String , Source ) > {
208188 let mut current = project_root. join ( relative_file_path) ;
209189 loop {
210- let parent_opt = current. parent ( ) . map ( |p| p. to_path_buf ( ) ) ;
211- let Some ( parent) = parent_opt else { break } ;
212- let parent_rel = parent. strip_prefix ( project_root) . unwrap_or ( parent. as_path ( ) ) ;
190+ if !current. pop ( ) {
191+ break ;
192+ }
193+ let parent_rel = current. strip_prefix ( project_root) . unwrap_or ( current. as_path ( ) ) ;
213194 if let Some ( rel_str) = parent_rel. to_str ( ) {
214195 if glob_list_matches ( rel_str, & config. ruby_package_paths ) {
215- let pkg_yml = parent . join ( "package.yml" ) ;
196+ let pkg_yml = current . join ( "package.yml" ) ;
216197 if pkg_yml. exists ( ) {
217198 if let Ok ( owner) = read_ruby_package_owner ( & pkg_yml) {
218199 if let Some ( team) = teams_by_name. get ( & owner) {
219200 let package_path = parent_rel. join ( "package.yml" ) ;
220- let package_glob = format ! ( "{}/**/**" , rel_str ) ;
201+ let package_glob = format ! ( "{rel_str }/**/**" ) ;
221202 return Some ( (
222203 team. name . clone ( ) ,
223204 Source :: Package ( package_path. to_string_lossy ( ) . to_string ( ) , package_glob) ,
@@ -227,12 +208,12 @@ fn nearest_package_owner(
227208 }
228209 }
229210 if glob_list_matches ( rel_str, & config. javascript_package_paths ) {
230- let pkg_json = parent . join ( "package.json" ) ;
211+ let pkg_json = current . join ( "package.json" ) ;
231212 if pkg_json. exists ( ) {
232213 if let Ok ( owner) = read_js_package_owner ( & pkg_json) {
233214 if let Some ( team) = teams_by_name. get ( & owner) {
234215 let package_path = parent_rel. join ( "package.json" ) ;
235- let package_glob = format ! ( "{}/**/**" , rel_str ) ;
216+ let package_glob = format ! ( "{rel_str }/**/**" ) ;
236217 return Some ( (
237218 team. name . clone ( ) ,
238219 Source :: Package ( package_path. to_string_lossy ( ) . to_string ( ) , package_glob) ,
@@ -242,20 +223,14 @@ fn nearest_package_owner(
242223 }
243224 }
244225 }
245- if parent == project_root {
226+ if current == project_root {
246227 break ;
247228 }
248- current = parent;
249229 }
250230 None
251231}
252232
253- fn source_directory_depth ( source : & Source ) -> usize {
254- match source {
255- Source :: Directory ( path) => path. matches ( '/' ) . count ( ) ,
256- _ => 0 ,
257- }
258- }
233+ // removed: use `Source::len()` instead
259234
260235fn glob_list_matches ( path : & str , globs : & [ String ] ) -> bool {
261236 globs. iter ( ) . any ( |g| glob_match ( g, path) )
@@ -351,11 +326,6 @@ mod tests {
351326 let file_at = td. path ( ) . join ( "at_form.rb" ) ;
352327 std:: fs:: write ( & file_at, "# @team Payroll\n puts 'x'\n " ) . unwrap ( ) ;
353328 assert_eq ! ( read_top_of_file_team( & file_at) , Some ( "Payroll" . to_string( ) ) ) ;
354-
355- // team: form (case-insensitive, allows spaces)
356- let file_colon = td. path ( ) . join ( "colon_form.rb" ) ;
357- std:: fs:: write ( & file_colon, "# Team: Payments\n puts 'y'\n " ) . unwrap ( ) ;
358- assert_eq ! ( read_top_of_file_team( & file_colon) , Some ( "Payments" . to_string( ) ) ) ;
359329 }
360330
361331 #[ test]
@@ -401,20 +371,12 @@ mod tests {
401371 // Ruby package
402372 let ruby_pkg = project_root. join ( "packs/payroll" ) ;
403373 std:: fs:: create_dir_all ( & ruby_pkg) . unwrap ( ) ;
404- std:: fs:: write (
405- ruby_pkg. join ( "package.yml" ) ,
406- "---\n owner: Payroll\n " ,
407- )
408- . unwrap ( ) ;
374+ std:: fs:: write ( ruby_pkg. join ( "package.yml" ) , "---\n owner: Payroll\n " ) . unwrap ( ) ;
409375
410376 // JS package
411377 let js_pkg = project_root. join ( "frontend/flow" ) ;
412378 std:: fs:: create_dir_all ( & js_pkg) . unwrap ( ) ;
413- std:: fs:: write (
414- js_pkg. join ( "package.json" ) ,
415- r#"{"metadata": {"owner": "UX"}}"# ,
416- )
417- . unwrap ( ) ;
379+ std:: fs:: write ( js_pkg. join ( "package.json" ) , r#"{"metadata": {"owner": "UX"}}"# ) . unwrap ( ) ;
418380
419381 // Teams map
420382 let mut tbn: HashMap < String , Team > = HashMap :: new ( ) ;
0 commit comments