use crate::parser::CodeOwnerRule;
use rustc_hash::FxHashSet;
pub fn validate_duplicates(rules: &[CodeOwnerRule]) -> Vec<CodeOwnerRule> {
let mut pattern_set = FxHashSet::default();
let mut original_path_set = FxHashSet::default();
pattern_set.reserve(rules.len());
original_path_set.reserve(rules.len());
let mut duplicates = Vec::new();
for rule in rules {
let is_original_path_duplicate = !original_path_set.insert(&rule.original_path);
let is_pattern_duplicate = !pattern_set.insert(&rule.pattern);
if is_original_path_duplicate || is_pattern_duplicate {
duplicates.push(rule.clone());
if !is_original_path_duplicate && is_pattern_duplicate {
println!("Warning: Duplicate pattern found in normalized pattern");
println!("Please raise an issue if this seems incorrect.");
println!("Pattern: {}", &rule.pattern);
println!("Original: {}", &rule.original_path);
}
}
}
duplicates
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::CodeOwnerRule;
fn rule(pattern: &str, original: &str) -> CodeOwnerRule {
CodeOwnerRule {
pattern: pattern.trim_matches('/').to_string(),
original_path: original.to_string(),
owners: vec!["@owner".to_string()],
}
}
#[test]
fn no_duplicates() {
let rules = vec![rule("a.txt", "a.txt"), rule("b.txt", "b.txt")];
let result = validate_duplicates(&rules);
assert!(result.is_empty());
}
#[test]
fn detects_exact_duplicates() {
let rules = vec![rule("src", "src/"), rule("src", "src/")];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 1);
assert_eq!(result[0].pattern, "src");
}
#[test]
fn normalized_duplicates_detected() {
let rules = vec![rule("docs", "/docs"), rule("docs", "docs")];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 1);
assert_eq!(result[0].pattern, "docs");
}
#[test]
fn different_slash_variations() {
let rules = vec![
rule("src/lib", "/src/lib/"),
rule("src/lib", "src/lib"),
rule("src/lib", "/src/lib"),
];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 2);
}
#[test]
fn original_path_duplicates() {
let rules = vec![rule("docs", "docs/"), rule("docs", "docs/")];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 1);
}
#[test]
fn wildcard_duplicates() {
let rules = vec![rule("*.md", "*.md"), rule("*.md", "*.md")];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 1);
}
#[test]
fn complex_pattern_duplicates() {
let rules = vec![
rule("**/*.test.js", "**/*.test.js"),
rule("src/*.rs", "src/*.rs"),
rule("**/*.test.js", "**/*.test.js"), ];
let result = validate_duplicates(&rules);
assert_eq!(result.len(), 1);
assert_eq!(result[0].pattern, "**/*.test.js");
}
}