specfold_core/config/
validate.rs1use super::types::RawConfig;
5use crate::workspace::owners::matcher;
6use regex::Regex;
7
8pub fn pattern_problem(raw: &RawConfig) -> Option<(String, String)> {
10 let regexes: [(&str, &[String]); 3] = [
11 ("[verify] serial", &raw.verify.serial),
12 ("[forbid] patterns", &raw.forbid.patterns),
13 ("[cites] history", &raw.cites.history),
14 ];
15 for (key, list) in regexes {
16 for r in list {
17 if let Err(e) = Regex::new(r) {
18 let why = e.to_string().lines().last().unwrap_or("").trim().to_string();
19 return Some((format!("{key} has \"{r}\", which is not a regular expression ({why})"), r.clone()));
20 }
21 }
22 }
23 let mut globs: Vec<(String, &[String])> = vec![
24 ("[verify] full_run_on".into(), &raw.verify.full_run_on),
25 ("[forbid] paths".into(), &raw.forbid.paths),
26 ("[check] count_paths".into(), &raw.check.count_paths),
27 ("[check] link_ignore".into(), &raw.check.link_ignore),
28 ("[cites] paths".into(), &raw.cites.paths),
29 ("[cites] exclude".into(), &raw.cites.exclude),
30 ("[public] paths".into(), &raw.public.paths),
31 ("[owners] retired".into(), &raw.owners.retired),
32 ("[owners] pending".into(), &raw.owners.pending),
33 ];
34 for g in &raw.guards {
35 globs.push((format!("guard '{}' paths", g.name), &g.paths));
36 globs.push((format!("guard '{}' exclude", g.name), &g.exclude));
37 globs.push((format!("guard '{}' deny_files", g.name), &g.deny_files));
38 }
39 for a in &raw.assets {
40 globs.push(("[[assets]] path".into(), std::slice::from_ref(&a.path)));
41 globs.push(("[[assets]] exclude".into(), &a.exclude));
42 }
43 for (key, list) in globs {
44 if let Some(p) = list.iter().find(|p| matcher(p).is_none()) {
45 return Some((
46 format!("{key} has \"{p}\", which is not a glob (check its brackets and braces)"),
47 p.clone(),
48 ));
49 }
50 }
51 None
52}