Skip to content

Commit 35fc992

Browse files
committed
Add validation for duplicate team entries
- Add explicit_leads() method to expose team leads - Add validate_duplicate_team_entries() to check for duplicates - Check leads, members, and alumni for duplicates - Collect all errors for better developer experience
1 parent 814a613 commit 35fc992

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

src/schema.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@ impl Team {
516516
pub(crate) fn explicit_alumni(&self) -> &[TeamMember] {
517517
self.people.alumni.as_ref().map_or(&[], Vec::as_slice)
518518
}
519+
520+
pub(crate) fn explicit_leads(&self) -> &[String] {
521+
&self.people.leads
522+
}
519523

520524
pub(crate) fn contains_person(&self, data: &Data, person: &Person) -> Result<bool, Error> {
521525
let members = self.members(data)?;

src/validate.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static CHECKS: &[Check<fn(&Data, &mut Vec<String>)>] = checks![
2828
validate_subteam_of,
2929
validate_team_leads,
3030
validate_team_members,
31+
validate_duplicate_team_entries,
3132
validate_alumni,
3233
validate_archived_teams,
3334
validate_inactive_members,
@@ -236,6 +237,67 @@ fn validate_team_members(data: &Data, errors: &mut Vec<String>) {
236237
});
237238
}
238239

240+
/// Helper for checking duplicates in a list
241+
fn check_duplicates<'a, I>(team_name: &str, label: &str, items: I) -> Result<(), Error>
242+
where
243+
I: IntoIterator<Item = &'a str>,
244+
{
245+
let mut seen = HashSet::new();
246+
let mut duplicates = HashSet::new();
247+
248+
for item in items {
249+
if !seen.insert(item) {
250+
duplicates.insert(item);
251+
}
252+
}
253+
254+
if !duplicates.is_empty() {
255+
let dup_list: Vec<&str> = duplicates.into_iter().collect();
256+
bail!(
257+
"team `{}` has duplicate {}: {}",
258+
team_name,
259+
label,
260+
dup_list.join(", ")
261+
);
262+
}
263+
264+
Ok(())
265+
}
266+
267+
/// Ensure no duplicate entries in team leads, members and alumni
268+
fn validate_duplicate_team_entries(data: &Data, errors: &mut Vec<String>) {
269+
wrapper(data.teams(), errors, |team, errors| {
270+
// Check leads for duplicates
271+
if let Err(e) = check_duplicates(
272+
team.name(),
273+
"leads",
274+
team.explicit_leads().iter().map(|s| s.as_str()),
275+
) {
276+
errors.push(e.to_string());
277+
}
278+
279+
// Check members for duplicates
280+
if let Err(e) = check_duplicates(
281+
team.name(),
282+
"members",
283+
team.explicit_members().iter().map(|m| m.github.as_str()),
284+
) {
285+
errors.push(e.to_string());
286+
}
287+
288+
// Check alumni for duplicates
289+
if let Err(e) = check_duplicates(
290+
team.name(),
291+
"alumni",
292+
team.explicit_alumni().iter().map(|a| a.github.as_str()),
293+
) {
294+
errors.push(e.to_string());
295+
}
296+
297+
Ok(())
298+
});
299+
}
300+
239301
/// Alumni team must consist only of automatically populated alumni from the other teams
240302
fn validate_alumni(data: &Data, errors: &mut Vec<String>) {
241303
let Some(alumni_team) = data.team("alumni") else {

0 commit comments

Comments
 (0)