Skip to main content

pedant_core/check_config/
gate.rs

1use std::collections::BTreeMap;
2
3use serde::Deserialize;
4
5/// Per-rule override from the `[gate]` TOML section.
6#[derive(Debug)]
7pub enum GateRuleOverride {
8    /// Suppresses the rule entirely.
9    Disabled,
10    /// Changes the rule's effective severity.
11    Severity(crate::gate::GateSeverity),
12}
13
14/// Deserialized `[gate]` section of `.pedant.toml`.
15///
16/// Keys are either `enabled` (master switch) or rule names mapped to
17/// `false` (disabled) or a severity string (`"deny"`, `"warn"`, `"info"`).
18#[derive(Debug)]
19pub struct GateConfig {
20    /// Master switch; `false` disables all gate rules.
21    pub enabled: bool,
22    /// Per-rule overrides keyed by rule name.
23    pub overrides: BTreeMap<Box<str>, GateRuleOverride>,
24}
25
26impl Default for GateConfig {
27    fn default() -> Self {
28        Self {
29            enabled: true,
30            overrides: BTreeMap::new(),
31        }
32    }
33}
34
35impl<'de> Deserialize<'de> for GateConfig {
36    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37    where
38        D: serde::Deserializer<'de>,
39    {
40        use serde::de::Error;
41
42        #[derive(Deserialize)]
43        #[serde(untagged)]
44        enum GateTomlValue {
45            Bool(bool),
46            String(String),
47        }
48
49        let raw: BTreeMap<Box<str>, GateTomlValue> = BTreeMap::deserialize(deserializer)?;
50        let mut enabled = true;
51        let mut overrides = BTreeMap::new();
52
53        for (key, value) in raw {
54            match (&*key, value) {
55                ("enabled", GateTomlValue::Bool(b)) => enabled = b,
56                ("enabled", GateTomlValue::String(_)) => {
57                    return Err(D::Error::custom("'enabled' must be a boolean"));
58                }
59                (_, _) if !is_known_gate_rule(&key) => {
60                    return Err(D::Error::custom(format!("unknown gate rule '{key}'")));
61                }
62                (_, GateTomlValue::Bool(false)) => {
63                    overrides.insert(key, GateRuleOverride::Disabled);
64                }
65                (_, GateTomlValue::Bool(true)) => {} // true = use default, no override
66                (_, GateTomlValue::String(s)) => {
67                    let severity = parse_gate_severity(&s).ok_or_else(|| {
68                        D::Error::custom(format!(
69                            "invalid gate severity '{s}': expected \"deny\", \"warn\", or \"info\""
70                        ))
71                    })?;
72                    overrides.insert(key, GateRuleOverride::Severity(severity));
73                }
74            }
75        }
76
77        Ok(GateConfig { enabled, overrides })
78    }
79}
80
81fn is_known_gate_rule(rule_name: &str) -> bool {
82    crate::gate::all_gate_rules()
83        .iter()
84        .any(|rule| rule.name == rule_name)
85}
86
87fn parse_gate_severity(s: &str) -> Option<crate::gate::GateSeverity> {
88    use crate::gate::GateSeverity;
89    match s {
90        "deny" => Some(GateSeverity::Deny),
91        "warn" => Some(GateSeverity::Warn),
92        "info" => Some(GateSeverity::Info),
93        _ => None,
94    }
95}