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}