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