Skip to content

Commit 595b86d

Browse files
committed
idiomatic improvements
1 parent 11d9506 commit 595b86d

File tree

3 files changed

+37
-69
lines changed

3 files changed

+37
-69
lines changed

src/ownership/for_file_fast.rs

Lines changed: 28 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ use std::{
66

77
use fast_glob::glob_match;
88
use 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

1412
use super::{FileOwner, mapper::Source};
1513

@@ -106,7 +104,7 @@ pub fn find_file_owners(project_root: &Path, config: &Config, file_path: &Path)
106104
}
107105

108106
fn 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> {
117115
fn 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

141135
fn 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

260235
fn 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\nputs '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\nputs '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-
"---\nowner: Payroll\n",
407-
)
408-
.unwrap();
374+
std::fs::write(ruby_pkg.join("package.yml"), "---\nowner: 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();

src/project.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,14 @@ mod tests {
202202

203203
#[test]
204204
fn test_vendored_gem_by_name_maps_all_gems() {
205-
let vg1 = VendoredGem { path: PathBuf::from("vendored/a"), name: "a".to_string() };
206-
let vg2 = VendoredGem { path: PathBuf::from("vendored/b"), name: "b".to_string() };
205+
let vg1 = VendoredGem {
206+
path: PathBuf::from("vendored/a"),
207+
name: "a".to_string(),
208+
};
209+
let vg2 = VendoredGem {
210+
path: PathBuf::from("vendored/b"),
211+
name: "b".to_string(),
212+
};
207213
let project = Project {
208214
base_path: PathBuf::from("."),
209215
files: vec![],

src/project_file_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a> ProjectFileBuilder<'a> {
4747
}
4848
}
4949

50-
fn build_project_file_without_cache(path: &PathBuf) -> ProjectFile {
50+
pub fn build_project_file_without_cache(path: &PathBuf) -> ProjectFile {
5151
let content = match std::fs::read_to_string(path) {
5252
Ok(content) => content,
5353
Err(_) => {

0 commit comments

Comments
 (0)