mit_commit_message_lints/lints/cmd/
read_lint_config.rs1use std::{
2 collections::{BTreeMap, BTreeSet},
3 convert::TryInto,
4};
5
6use miette::{IntoDiagnostic, Result, SourceSpan};
7use mit_lint::{Lint, Lints, CONFIG_KEY_PREFIX};
8
9use crate::{external::Vcs, lints::cmd::errors::SerialiseLintError};
10
11pub fn read_from_toml_or_else_vcs(config: &str, vcs: &dyn Vcs) -> Result<Lints> {
20 let vcs_lints = try_from_vcs(vcs)?;
21 let config: BTreeMap<String, BTreeMap<String, BTreeMap<String, bool>>> = toml::from_str(config)
23 .map_err(|x| SerialiseLintError {
24 src: config.to_string(),
25 message: x.to_string(),
26 span: x
27 .span()
28 .map_or_else(|| SourceSpan::new(0.into(), 0), Into::into),
29 })?;
30
31 let lint_prefix = CONFIG_KEY_PREFIX.split('.').collect::<Vec<_>>();
32 let namespace = (*lint_prefix.first().unwrap()).to_string();
33
34 let Some(config) = config.get(&namespace) else {
35 return Ok(vcs_lints);
36 };
37
38 let group = (*lint_prefix.get(1).unwrap()).to_string();
39
40 let Some(lint_names) = config.get(&group) else {
41 return Ok(vcs_lints);
42 };
43
44 let to_add: Lints = lint_names
45 .iter()
46 .filter_map(|(key, value)| if *value { Some(key as &str) } else { None })
47 .collect::<Vec<&str>>()
48 .try_into()
49 .into_diagnostic()?;
50
51 let to_remove: Lints = lint_names
52 .iter()
53 .filter_map(|(key, value)| if *value { None } else { Some(key as &str) })
54 .collect::<Vec<&str>>()
55 .try_into()
56 .into_diagnostic()?;
57
58 Ok(vcs_lints.subtract(&to_remove).merge(&to_add))
59}
60
61fn try_from_vcs(config: &dyn Vcs) -> Result<Lints> {
66 Ok(Lints::new(
67 Lint::all_lints()
68 .filter_map(|lint| {
69 get_config_or_default(config, lint, lint.enabled_by_default()).transpose()
70 })
71 .collect::<Result<BTreeSet<Lint>>>()?,
72 ))
73}
74
75fn get_config_or_default(config: &dyn Vcs, lint: Lint, default: bool) -> Result<Option<Lint>> {
76 Ok(config
77 .get_bool(&lint.config_key())?
78 .or(Some(default))
79 .filter(|lint_value| lint_value == &true)
80 .map(|_| lint))
81}
82
83#[cfg(test)]
84mod tests {
85 use mit_lint::Lint;
86 use std::collections::BTreeMap;
87
88 use crate::{external::InMemory, lints::cmd::read_lint_config::read_from_toml_or_else_vcs};
89
90 #[test]
91 fn explicitly_enabled_lint_from_vcs_is_included() {
92 let mut strings = BTreeMap::new();
93 strings.insert("mit.lint.pivotal-tracker-id-missing".into(), "true".into());
94 let vcs = InMemory::new(&mut strings);
95
96 let lints = read_from_toml_or_else_vcs("", &vcs).unwrap();
97
98 let found = lints
101 .into_iter()
102 .any(|l| l == Lint::PivotalTrackerIdMissing);
103 assert!(found, "Enabled lint should be included in results");
104 }
105}