Skip to main content

specfold_core/config/
validate.rs

1//! C237: patterns in spec.toml are checked when the file loads. A regex or glob that does not
2//! compile used to be dropped where it was used, so the setting silently did nothing.
3
4use super::types::RawConfig;
5use crate::workspace::owners::matcher;
6use regex::Regex;
7
8/// The first pattern that does not compile: why, and the value as written (to find its line).
9pub 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}