mdbook_lint_core/
config.rs

1//! Core configuration types for mdbook-lint-core
2//!
3//! This module contains the minimal configuration types needed by the core
4//! linting engine. The full configuration is handled by the CLI crate.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Core configuration for the linting engine
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Config {
12    /// List of enabled rule categories
13    #[serde(rename = "enabled-categories", default)]
14    pub enabled_categories: Vec<String>,
15
16    /// List of disabled rule categories
17    #[serde(rename = "disabled-categories", default)]
18    pub disabled_categories: Vec<String>,
19
20    /// List of explicitly enabled rules
21    #[serde(rename = "enabled-rules", default)]
22    pub enabled_rules: Vec<String>,
23
24    /// List of explicitly disabled rules
25    #[serde(rename = "disabled-rules", default)]
26    pub disabled_rules: Vec<String>,
27
28    /// How to handle deprecated rule warnings
29    #[serde(rename = "deprecated-warning", default)]
30    pub deprecated_warning: DeprecatedWarningLevel,
31
32    /// Enable markdownlint compatibility mode (disables rules that are disabled by default in markdownlint)
33    #[serde(rename = "markdownlint-compatible", default)]
34    pub markdownlint_compatible: bool,
35
36    /// Global auto-fix setting (default: true when --fix is used)
37    /// Can be overridden per-rule in rule-specific configuration
38    #[serde(rename = "auto-fix", default = "default_auto_fix")]
39    pub auto_fix: bool,
40
41    /// Rule-specific configuration
42    #[serde(flatten)]
43    pub rule_configs: HashMap<String, toml::Value>,
44}
45
46fn default_auto_fix() -> bool {
47    true
48}
49
50impl Default for Config {
51    fn default() -> Self {
52        Self {
53            enabled_categories: Vec::new(),
54            disabled_categories: Vec::new(),
55            enabled_rules: Vec::new(),
56            disabled_rules: Vec::new(),
57            deprecated_warning: DeprecatedWarningLevel::default(),
58            markdownlint_compatible: false,
59            auto_fix: true, // Default to true - fixes are applied when --fix is used
60            rule_configs: HashMap::new(),
61        }
62    }
63}
64
65/// How to handle deprecated rule warnings
66#[derive(Debug, Clone, Serialize, Deserialize, Default)]
67#[serde(rename_all = "lowercase")]
68pub enum DeprecatedWarningLevel {
69    /// Show warning messages for deprecated rules (default)
70    #[default]
71    Warn,
72    /// Show info messages for deprecated rules
73    Info,
74    /// Don't show any messages for deprecated rules
75    Silent,
76}
77
78impl Config {
79    /// Check if a rule should be run based on configuration
80    pub fn should_run_rule(
81        &self,
82        rule_id: &str,
83        rule_category: &str,
84        rule_enabled_by_default: bool,
85    ) -> bool {
86        // Explicit rule configuration takes precedence
87        if self.enabled_rules.contains(&rule_id.to_string()) {
88            return true;
89        }
90        if self.disabled_rules.contains(&rule_id.to_string()) {
91            return false;
92        }
93
94        // Category configuration takes precedence over default
95        if self.enabled_categories.contains(&rule_category.to_string()) {
96            return true;
97        }
98        if self
99            .disabled_categories
100            .contains(&rule_category.to_string())
101        {
102            return false;
103        }
104
105        // Use default enabled state
106        rule_enabled_by_default
107    }
108
109    /// Check if auto-fix is enabled for a specific rule
110    ///
111    /// Returns true if:
112    /// 1. The rule has `auto-fix = true` in its config, OR
113    /// 2. The rule has no `auto-fix` setting and global `auto-fix` is true (default)
114    ///
115    /// Returns false if:
116    /// 1. The rule has `auto-fix = false` in its config, OR
117    /// 2. The rule has no `auto-fix` setting and global `auto-fix` is false
118    pub fn should_auto_fix_rule(&self, rule_id: &str) -> bool {
119        // Check for rule-specific auto-fix setting
120        if let Some(rule_config) = self.rule_configs.get(rule_id)
121            && let Some(auto_fix) = rule_config.get("auto-fix").and_then(|v| v.as_bool())
122        {
123            return auto_fix;
124        }
125
126        // Fall back to global setting
127        self.auto_fix
128    }
129}