codeowners_validation/validators/
validator.rs1use crate::parser::CodeOwnerRule;
2use crate::validators::duplicate_patterns::validate_duplicates;
3use crate::validators::exists::validate_directory;
4use std::path::Path;
5use std::time;
6
7#[derive(Debug, Clone, Default)]
8pub struct ValidatorArgs {
9 pub exists: bool,
10 pub duplicate_patterns: bool,
11}
12
13impl ValidatorArgs {
14 pub fn from_env(args_str: &str) -> Self {
15 let mut args = ValidatorArgs::default();
16
17 for arg in args_str.split(',') {
18 match arg.trim() {
19 "exists" => args.exists = true,
20 "duplicate_patterns" => args.duplicate_patterns = true,
21 "all" => {
22 args.exists = true;
23 args.duplicate_patterns = true;
24 }
25 _ => (),
26 }
27 }
28
29 args
30 }
31
32 pub fn should_run_all(&self) -> bool {
33 !self.exists && !self.duplicate_patterns
34 }
35}
36
37type ValidatorFn = fn(&[CodeOwnerRule]) -> Vec<CodeOwnerRule>;
38
39pub fn run_validator(
40 args: &ValidatorArgs,
41 rules: &[CodeOwnerRule],
42) -> Vec<(String, CodeOwnerRule)> {
43 let mut failed_rules = Vec::new();
44
45 let validators: Vec<(&str, ValidatorFn)> = vec![
46 ("exists", |rules| {
47 let repo_dir = Path::new(".");
48 match validate_directory(repo_dir, rules) {
49 Ok(result) => result,
50 Err(err) => {
51 eprintln!("❌ Error during 'exists' validation: {}", err);
52 Vec::new()
53 }
54 }
55 }),
56 ("duplicate_patterns", validate_duplicates),
57 ];
58
59 for (name, validator_fn) in validators {
60 if args.should_run_all()
61 || (name == "exists" && args.exists)
62 || (name == "duplicate_patterns" && args.duplicate_patterns)
63 {
64 let now = time::Instant::now();
65 let results = validator_fn(rules);
66 let num_failures = results.len();
67
68 for rule in results {
69 failed_rules.push((name.to_string(), rule));
70 }
71
72 println!(
73 "✓ {} validation completed in {:?} ({} issues found)",
74 name,
75 now.elapsed(),
76 num_failures
77 );
78 }
79 }
80
81 failed_rules
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::parser::CodeOwnerRule;
88
89 fn rule(pattern: &str, original: &str) -> CodeOwnerRule {
90 CodeOwnerRule {
91 pattern: pattern.trim_matches('/').to_string(),
92 original_path: original.to_string(),
93 owners: vec!["@x".to_string()],
94 }
95 }
96
97 #[test]
98 fn runs_all_by_default() {
99 let rules = vec![
100 rule("missing1.txt", "missing1.txt"),
101 rule("dup.txt", "dup.txt"),
102 rule("dup.txt", "dup.txt"),
103 ];
104 let args = ValidatorArgs::default();
105 let failures = run_validator(&args, &rules);
106 assert!(!failures.is_empty());
107 }
108
109 #[test]
110 fn runs_only_exists_when_enabled() {
111 let rules = vec![rule("notfound.txt", "notfound.txt")];
112 let args = ValidatorArgs {
113 exists: true,
114 duplicate_patterns: false,
115 };
116 let failures = run_validator(&args, &rules);
117 assert_eq!(failures.len(), 1);
118 assert_eq!(failures[0].0, "exists");
119 }
120
121 #[test]
122 fn runs_only_duplicates_when_enabled() {
123 let rules = vec![rule("x.txt", "x.txt"), rule("x.txt", "x.txt")];
124 let args = ValidatorArgs {
125 exists: false,
126 duplicate_patterns: true,
127 };
128 let failures = run_validator(&args, &rules);
129 assert_eq!(failures.len(), 1);
130 assert_eq!(failures[0].0, "duplicate_patterns");
131 }
132
133 #[test]
134 fn from_env_splits_checks() {
135 let args = ValidatorArgs::from_env("exists,duplicate_patterns");
136 assert!(args.exists);
137 assert!(args.duplicate_patterns);
138 }
139
140 #[test]
141 fn from_env_handles_all() {
142 let args = ValidatorArgs::from_env("all");
143 assert!(args.exists);
144 assert!(args.duplicate_patterns);
145 }
146
147 #[test]
148 fn from_env_handles_whitespace() {
149 let args = ValidatorArgs::from_env(" exists , duplicate_patterns ");
150 assert!(args.exists);
151 assert!(args.duplicate_patterns);
152 }
153
154 #[test]
155 fn should_run_all_when_none_specified() {
156 let args = ValidatorArgs::default();
157 assert!(args.should_run_all());
158 }
159
160 #[test]
161 fn not_should_run_all_when_any_specified() {
162 let args = ValidatorArgs {
163 exists: true,
164 duplicate_patterns: false,
165 };
166 assert!(!args.should_run_all());
167 }
168}