Skip to content

Commit e888245

Browse files
committed
memoize codeowners
1 parent f708aa9 commit e888245

File tree

5 files changed

+164
-41
lines changed

5 files changed

+164
-41
lines changed

Cargo.lock

+87-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ clap_derive = "4.5.18"
1515
error-stack = "0.5.0"
1616
enum_dispatch = "0.3.13"
1717
fast-glob = "0.4.0"
18+
glob = "0.3.2"
1819
ignore = "0.4.23"
1920
itertools = "0.14.0"
2021
lazy_static = "1.5.0"
22+
memoize = "0.5.1"
2123
path-clean = "1.0.1"
2224
rayon = "1.10.0"
2325
regex = "1.11.1"
@@ -30,7 +32,6 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
3032

3133
[dev-dependencies]
3234
assert_cmd = "2.0.16"
33-
glob = "0.3.1"
3435
rusty-hook = "^0.11.2"
3536
predicates = "3.1.2"
3637
pretty_assertions = "1.4.1" # Shows a more readable diff when comparing objects

src/ownership.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ use mapper::{OwnerMatcher, Source, TeamName};
44
use std::{
55
error::Error,
66
fmt::{self, Display},
7-
path::{Path, PathBuf},
7+
path::Path,
88
sync::Arc,
99
};
1010
use tracing::{info, instrument};
1111

1212
mod file_generator;
1313
mod file_owner_finder;
1414
pub(crate) mod mapper;
15-
mod parser;
15+
pub(crate) mod parser;
1616
mod validator;
1717

1818
use crate::{
@@ -178,10 +178,6 @@ impl Ownership {
178178
}
179179
}
180180

181-
pub fn fast_team_name_from_file_path(file_path: &str, code_owners_file_path: &PathBuf) -> Result<Option<String>, Box<dyn Error>> {
182-
parser::team_name_from_file_path(Path::new(file_path), code_owners_file_path)
183-
}
184-
185181
#[cfg(test)]
186182
mod tests {
187183
use super::*;

src/ownership/parser.rs

+62-24
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,78 @@
1-
use crate::ownership::{FileGenerator, TeamOwnership};
1+
use crate::{
2+
ownership::{FileGenerator, TeamOwnership},
3+
project::Team,
4+
};
25
use fast_glob::glob_match;
6+
use memoize::memoize;
37
use std::{
8+
collections::HashMap,
49
error::Error,
510
fs,
611
io::Error as IoError,
712
path::{Path, PathBuf},
813
};
914

10-
pub fn team_name_from_file_path(file_path: &Path, codeowners_file_path: &PathBuf) -> Result<Option<String>, Box<dyn Error>> {
11-
let file_path_str = file_path
12-
.to_str()
13-
.ok_or(IoError::new(std::io::ErrorKind::InvalidInput, "Invalid file path"))?;
14-
let slash_prefixed = if file_path_str.starts_with("/") {
15-
file_path_str.to_string()
16-
} else {
17-
format!("/{}", file_path_str)
18-
};
19-
20-
let codeowners_lines_in_priorty = build_codeowners_lines_in_priority(codeowners_file_path)?;
21-
22-
for line in codeowners_lines_in_priorty {
23-
let (glob, team_name) = line
24-
.split_once(' ')
25-
.ok_or(IoError::new(std::io::ErrorKind::InvalidInput, "Invalid line"))?;
26-
if glob_match(glob, &slash_prefixed) {
27-
return Ok(Some(team_name.to_string()));
15+
pub struct Parser {
16+
pub project_root: PathBuf,
17+
pub codeowners_file_path: PathBuf,
18+
}
19+
20+
impl Parser {
21+
pub fn team_from_file_path(&self, file_path: &Path) -> Result<Option<Team>, Box<dyn Error>> {
22+
let file_path_str = file_path
23+
.to_str()
24+
.ok_or(IoError::new(std::io::ErrorKind::InvalidInput, "Invalid file path"))?;
25+
let slash_prefixed = if file_path_str.starts_with("/") {
26+
file_path_str.to_string()
27+
} else {
28+
format!("/{}", file_path_str)
29+
};
30+
31+
let codeowners_lines_in_priorty = build_codeowners_lines_in_priority(self.codeowners_file_path.to_string_lossy().into_owned());
32+
for line in codeowners_lines_in_priorty {
33+
let (glob, team_name) = line
34+
.split_once(' ')
35+
.ok_or(IoError::new(std::io::ErrorKind::InvalidInput, "Invalid line"))?;
36+
if glob_match(glob, &slash_prefixed) {
37+
let tbn = teams_by_name(self.absolute_team_files_glob());
38+
let team: Option<Team> = tbn.get(team_name.to_string().as_str()).cloned();
39+
return Ok(team);
40+
}
2841
}
42+
43+
Ok(None)
44+
}
45+
46+
fn absolute_team_files_glob(&self) -> String {
47+
self.project_root
48+
.join(self.codeowners_file_path.to_string_lossy().into_owned())
49+
.to_string_lossy()
50+
.into_owned()
51+
}
52+
}
53+
54+
#[memoize]
55+
fn teams_by_name(team_file_glob: String) -> HashMap<String, Team> {
56+
let mut teams = HashMap::new();
57+
58+
// Get all files matching the glob pattern
59+
let paths = glob::glob(&team_file_glob)
60+
.expect("Failed to read glob pattern")
61+
.filter_map(Result::ok);
62+
63+
// Process each matching file
64+
for path in paths {
65+
let team = Team::from_team_file_path(path).unwrap();
66+
teams.insert(team.name.clone(), team);
2967
}
3068

31-
Ok(None)
69+
teams
3270
}
3371

34-
fn build_codeowners_lines_in_priority(codeowners_file_path: &PathBuf) -> Result<Vec<String>, Box<dyn Error>> {
35-
let codeowners_file = fs::read_to_string(codeowners_file_path)?;
36-
let stripped_lines = stripped_lines_by_priority(&codeowners_file);
37-
Ok(stripped_lines)
72+
#[memoize]
73+
fn build_codeowners_lines_in_priority(codeowners_file_path: String) -> Vec<String> {
74+
let codeowners_file = fs::read_to_string(codeowners_file_path).unwrap();
75+
stripped_lines_by_priority(&codeowners_file)
3876
}
3977

4078
fn stripped_lines_by_priority(codeowners_file: &str) -> Vec<String> {

src/runner.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use core::fmt;
2-
use std::{fs::File, path::PathBuf};
2+
use std::{
3+
fs::File,
4+
path::{Path, PathBuf},
5+
};
36

47
use error_stack::{Context, Result, ResultExt};
58
use serde::{Deserialize, Serialize};
69

710
use crate::{
811
cache::{Cache, Caching, file::GlobalCache, noop::NoopCache},
912
config::Config,
10-
ownership::{FileOwner, Ownership, fast_team_name_from_file_path},
13+
ownership::{FileOwner, Ownership},
1114
project_builder::ProjectBuilder,
1215
};
1316

@@ -35,10 +38,14 @@ pub fn for_file(run_config: &RunConfig, file_path: &str, verbose: bool) -> RunRe
3538
if verbose {
3639
run_with_runner(run_config, |runner| runner.for_file(file_path))
3740
} else {
38-
let result = fast_team_name_from_file_path(file_path, &run_config.codeowners_file_path);
41+
let parser = crate::ownership::parser::Parser {
42+
project_root: run_config.project_root.clone(),
43+
codeowners_file_path: run_config.codeowners_file_path.clone(),
44+
};
45+
let result = parser.team_from_file_path(Path::new(file_path));
3946
match result {
4047
Ok(Some(team_name)) => RunResult {
41-
info_messages: vec![format!("{}", team_name)],
48+
info_messages: vec![format!("{}", team_name.name)],
4249
..Default::default()
4350
},
4451
Ok(None) => RunResult {

0 commit comments

Comments
 (0)