Skip to content

Commit 5f4a201

Browse files
committed
introducing concept of subtracted globs
1 parent 97634a2 commit 5f4a201

File tree

5 files changed

+132
-66
lines changed

5 files changed

+132
-66
lines changed

src/ownership/mapper.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,49 @@ impl Source {
6767
#[derive(Debug, PartialEq)]
6868
pub enum OwnerMatcher {
6969
ExactMatches(HashMap<PathBuf, TeamName>, Source),
70-
Glob { glob: String, team_name: TeamName, source: Source },
70+
Glob {
71+
glob: String,
72+
subtracted_globs: Vec<String>,
73+
team_name: TeamName,
74+
source: Source,
75+
},
7176
}
7277

7378
impl OwnerMatcher {
79+
pub fn new_glob_with_candidate_subtracted_globs(
80+
glob: String,
81+
candidate_subtracted_globs: Vec<String>,
82+
team_name: TeamName,
83+
source: Source,
84+
) -> Self {
85+
let subtracted_globs = candidate_subtracted_globs.iter().filter(|candidate_subtracted_glob| {
86+
glob_match(candidate_subtracted_glob, &glob) || glob_match(&glob, candidate_subtracted_glob)
87+
}).cloned().collect();
88+
OwnerMatcher::Glob {
89+
glob,
90+
subtracted_globs,
91+
team_name,
92+
source,
93+
}
94+
}
95+
96+
pub fn new_glob(glob: String, team_name: TeamName, source: Source) -> Self {
97+
OwnerMatcher::Glob {
98+
glob,
99+
subtracted_globs: vec![],
100+
team_name,
101+
source,
102+
}
103+
}
104+
74105
pub fn owner_for(&self, relative_path: &Path) -> (Option<&TeamName>, &Source) {
75106
match self {
76-
OwnerMatcher::Glob { glob, team_name, source } => {
107+
OwnerMatcher::Glob {
108+
glob,
109+
subtracted_globs,
110+
team_name,
111+
source,
112+
} => {
77113
if glob_match(glob, relative_path.to_str().unwrap()) {
78114
(Some(team_name), source)
79115
} else {
@@ -94,6 +130,7 @@ mod tests {
94130
let team_name = "team1".to_string();
95131
let owner_matcher = OwnerMatcher::Glob {
96132
glob: glob.to_string(),
133+
subtracted_globs: vec![],
97134
team_name: team_name.clone(),
98135
source: source.clone(),
99136
};
@@ -150,4 +187,30 @@ mod tests {
150187
);
151188
assert_eq!(Source::TeamYml.to_string(), "Teams own their configuration files");
152189
}
190+
191+
#[test]
192+
fn test_new_glob_with_candidate_subtracted_globs() {
193+
assert_new_glob_with_candidate_subtracted_globs("packs/bam/**/**", &[], &[]);
194+
assert_new_glob_with_candidate_subtracted_globs("packs/bam/**/**", &["packs/bam/app/**/**"], &["packs/bam/app/**/**"]);
195+
assert_new_glob_with_candidate_subtracted_globs("packs/bam/**/**", &["packs/bam/app/an/exceptional/path/it.rb"], &["packs/bam/app/an/exceptional/path/it.rb"]);
196+
assert_new_glob_with_candidate_subtracted_globs("packs/bam/**/**", &["packs/bam.rb"], &[]);
197+
assert_new_glob_with_candidate_subtracted_globs("packs/bam/**/**", &["packs/nope/app/**/**"], &[]);
198+
assert_new_glob_with_candidate_subtracted_globs("packs/**", &["packs/yep/app/**/**"], &["packs/yep/app/**/**"]);
199+
assert_new_glob_with_candidate_subtracted_globs("packs/foo.yml", &["packs/foo/**/**"], &[]);
200+
}
201+
202+
fn assert_new_glob_with_candidate_subtracted_globs(glob: &str, candidate_subtracted_globs: &[&str], expected_subtracted_globs: &[&str]) {
203+
let owner_matcher = OwnerMatcher::new_glob_with_candidate_subtracted_globs(
204+
glob.to_string(),
205+
candidate_subtracted_globs.iter().map(|s| s.to_string()).collect(),
206+
"team1".to_string(),
207+
Source::TeamGlob(glob.to_string()),
208+
);
209+
210+
if let OwnerMatcher::Glob { subtracted_globs, .. } = owner_matcher {
211+
assert_eq!(subtracted_globs, expected_subtracted_globs);
212+
} else {
213+
panic!("Expected a Glob matcher");
214+
}
215+
}
153216
}

src/ownership/mapper/directory_mapper.rs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ impl Mapper for DirectoryMapper {
4040
let mut owner_matchers = Vec::new();
4141

4242
for file in &self.project.directory_codeowner_files {
43-
owner_matchers.push(OwnerMatcher::Glob {
44-
glob: format!("{}/**/**", escape_brackets(&file.directory_root().to_string_lossy())),
45-
team_name: file.owner.to_owned(),
46-
source: Source::Directory(file.directory_root().to_string_lossy().to_string()),
47-
});
43+
owner_matchers.push(OwnerMatcher::new_glob(
44+
format!("{}/**/**", escape_brackets(&file.directory_root().to_string_lossy())),
45+
file.owner.to_owned(),
46+
Source::Directory(file.directory_root().to_string_lossy().to_string()),
47+
));
4848
}
4949

5050
owner_matchers
@@ -125,21 +125,21 @@ mod tests {
125125
vecs_match(
126126
&mapper.owner_matchers(),
127127
&vec![
128-
OwnerMatcher::Glob {
129-
glob: "app/consumers/**/**".to_owned(),
130-
team_name: "Bar".to_owned(),
131-
source: Source::Directory("app/consumers".to_string()),
132-
},
133-
OwnerMatcher::Glob {
134-
glob: "app/services/**/**".to_owned(),
135-
team_name: "Foo".to_owned(),
136-
source: Source::Directory("app/services".to_owned()),
137-
},
138-
OwnerMatcher::Glob {
139-
glob: "app/services/exciting/**/**".to_owned(),
140-
team_name: "Bar".to_owned(),
141-
source: Source::Directory("app/services/exciting".to_owned()),
142-
},
128+
OwnerMatcher::new_glob(
129+
"app/consumers/**/**".to_owned(),
130+
"Bar".to_owned(),
131+
Source::Directory("app/consumers".to_string()),
132+
),
133+
OwnerMatcher::new_glob(
134+
"app/services/**/**".to_owned(),
135+
"Foo".to_owned(),
136+
Source::Directory("app/services".to_owned()),
137+
),
138+
OwnerMatcher::new_glob(
139+
"app/services/exciting/**/**".to_owned(),
140+
"Bar".to_owned(),
141+
Source::Directory("app/services/exciting".to_owned()),
142+
),
143143
],
144144
);
145145
Ok(())
@@ -152,16 +152,16 @@ mod tests {
152152
vecs_match(
153153
&mapper.owner_matchers(),
154154
&vec![
155-
OwnerMatcher::Glob {
156-
glob: "app/\\[consumers\\]/**/**".to_string(),
157-
team_name: "Bar".to_string(),
158-
source: Source::Directory("app/[consumers]".to_string()),
159-
},
160-
OwnerMatcher::Glob {
161-
glob: "app/\\[consumers\\]/deep/nesting/\\[nestdir\\]/**/**".to_string(),
162-
team_name: "Foo".to_string(),
163-
source: Source::Directory("app/[consumers]/deep/nesting/[nestdir]".to_string()),
164-
},
155+
OwnerMatcher::new_glob(
156+
"app/\\[consumers\\]/**/**".to_string(),
157+
"Bar".to_string(),
158+
Source::Directory("app/[consumers]".to_string()),
159+
),
160+
OwnerMatcher::new_glob(
161+
"app/\\[consumers\\]/deep/nesting/\\[nestdir\\]/**/**".to_string(),
162+
"Foo".to_string(),
163+
Source::Directory("app/[consumers]/deep/nesting/[nestdir]".to_string()),
164+
),
165165
],
166166
);
167167
Ok(())

src/ownership/mapper/package_mapper.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ impl PackageMapper {
101101
let team = team_by_name.get(&package.owner);
102102

103103
if let Some(team) = team {
104-
owner_matchers.push(OwnerMatcher::Glob {
105-
glob: format!("{}/**/**", package_root),
106-
team_name: team.name.to_owned(),
107-
source: Source::Package(package.path.to_string_lossy().to_string(), format!("{}/**/**", package_root)),
108-
});
104+
owner_matchers.push(OwnerMatcher::new_glob(
105+
format!("{}/**/**", package_root),
106+
team.name.to_owned(),
107+
Source::Package(package.path.to_string_lossy().to_string(), format!("{}/**/**", package_root)),
108+
));
109109
}
110110
}
111111

@@ -198,16 +198,16 @@ mod tests {
198198
vecs_match(
199199
&mapper.owner_matchers(&PackageType::Ruby),
200200
&vec![
201-
OwnerMatcher::Glob {
202-
glob: "packs/bam/**/**".to_owned(),
203-
team_name: "Bam".to_owned(),
204-
source: Source::Package("packs/bam/package.yml".to_owned(), "packs/bam/**/**".to_owned()),
205-
},
206-
OwnerMatcher::Glob {
207-
glob: "packs/foo/**/**".to_owned(),
208-
team_name: "Baz".to_owned(),
209-
source: Source::Package("packs/foo/package.yml".to_owned(), "packs/foo/**/**".to_owned()),
210-
},
201+
OwnerMatcher::new_glob(
202+
"packs/bam/**/**".to_owned(),
203+
"Bam".to_owned(),
204+
Source::Package("packs/bam/package.yml".to_owned(), "packs/bam/**/**".to_owned()),
205+
),
206+
OwnerMatcher::new_glob(
207+
"packs/foo/**/**".to_owned(),
208+
"Baz".to_owned(),
209+
Source::Package("packs/foo/package.yml".to_owned(), "packs/foo/**/**".to_owned()),
210+
),
211211
],
212212
);
213213
Ok(())

src/ownership/mapper/team_gem_mapper.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ impl Mapper for TeamGemMapper {
4646
let vendored_gem = vendored_gem_by_name.get(owned_gem);
4747

4848
if let Some(vendored_gem) = vendored_gem {
49-
owner_matchers.push(OwnerMatcher::Glob {
50-
glob: format!("{}/**/*", self.project.relative_path(&vendored_gem.path).to_string_lossy()),
51-
team_name: team.name.clone(),
52-
source: Source::TeamGem,
53-
});
49+
owner_matchers.push(OwnerMatcher::new_glob(
50+
format!("{}/**/*", self.project.relative_path(&vendored_gem.path).to_string_lossy()),
51+
team.name.clone(),
52+
Source::TeamGem,
53+
));
5454
}
5555
}
5656
}
@@ -92,11 +92,11 @@ mod tests {
9292
let mapper = TeamGemMapper::build(ownership.project.clone());
9393
vecs_match(
9494
&mapper.owner_matchers(),
95-
&vec![OwnerMatcher::Glob {
96-
glob: "gems/globbing/**/*".to_owned(),
97-
team_name: "Bam".to_owned(),
98-
source: Source::TeamGem,
99-
}],
95+
&vec![OwnerMatcher::new_glob(
96+
"gems/globbing/**/*".to_owned(),
97+
"Bam".to_owned(),
98+
Source::TeamGem,
99+
)],
100100
);
101101
Ok(())
102102
}

src/ownership/mapper/team_glob_mapper.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ impl Mapper for TeamGlobMapper {
3636

3737
fn owner_matchers(&self) -> Vec<OwnerMatcher> {
3838
self.iter_team_globs()
39-
.map(|(glob, github_team, _, _)| OwnerMatcher::Glob {
40-
glob: glob.to_owned(),
41-
team_name: github_team.to_owned(),
42-
source: Source::TeamGlob(glob.to_owned()),
39+
.map(|(glob, github_team, _, _)| {
40+
OwnerMatcher::new_glob_with_candidate_subtracted_globs(
41+
glob.to_owned(),
42+
vec![],
43+
github_team.to_owned(),
44+
Source::TeamGlob(glob.to_owned()),
45+
)
4346
})
4447
.collect()
4548
}
@@ -78,11 +81,11 @@ mod tests {
7881
let mapper = TeamGlobMapper::build(ownership.project.clone());
7982
vecs_match(
8083
&mapper.owner_matchers(),
81-
&vec![OwnerMatcher::Glob {
82-
glob: "packs/bar/**".to_owned(),
83-
team_name: "@Baz".to_owned(),
84-
source: Source::TeamGlob("packs/bar/**".to_owned()),
85-
}],
84+
&vec![OwnerMatcher::new_glob(
85+
"packs/bar/**".to_owned(),
86+
"@Baz".to_owned(),
87+
Source::TeamGlob("packs/bar/**".to_owned()),
88+
)],
8689
);
8790
Ok(())
8891
}

0 commit comments

Comments
 (0)