Skip to main content

rumdl_lib/rules/md010_no_hard_tabs/
md010_config.rs

1use crate::rule_config_serde::RuleConfig;
2use crate::types::PositiveUsize;
3use serde::{Deserialize, Serialize};
4
5/// Configuration for MD010 (No hard tabs)
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7#[serde(rename_all = "kebab-case")]
8pub struct MD010Config {
9    /// Number of spaces per tab (default: 4)
10    #[serde(default = "default_spaces_per_tab", alias = "spaces_per_tab")]
11    pub spaces_per_tab: PositiveUsize,
12
13    /// Check for hard tabs inside code blocks (default: false).
14    /// When false, tabs inside fenced and indented code blocks are skipped.
15    #[serde(default = "default_code_blocks", alias = "code_blocks")]
16    pub code_blocks: bool,
17}
18
19fn default_spaces_per_tab() -> PositiveUsize {
20    PositiveUsize::from_const(4)
21}
22
23fn default_code_blocks() -> bool {
24    false
25}
26
27impl Default for MD010Config {
28    fn default() -> Self {
29        Self {
30            spaces_per_tab: default_spaces_per_tab(),
31            code_blocks: default_code_blocks(),
32        }
33    }
34}
35
36impl RuleConfig for MD010Config {
37    const RULE_NAME: &'static str = "MD010";
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_default_config() {
46        let config = MD010Config::default();
47        assert_eq!(config.spaces_per_tab.get(), 4);
48    }
49
50    #[test]
51    fn test_config_deserialization() {
52        let toml_str = r#"
53            spaces-per-tab = 2
54        "#;
55        let config: MD010Config = toml::from_str(toml_str).unwrap();
56        assert_eq!(config.spaces_per_tab.get(), 2);
57    }
58
59    #[test]
60    fn test_snake_case_backwards_compatibility() {
61        let toml_str = r#"
62            spaces_per_tab = 8
63        "#;
64        let config: MD010Config = toml::from_str(toml_str).unwrap();
65        assert_eq!(config.spaces_per_tab.get(), 8);
66    }
67
68    #[test]
69    fn test_validation_error() {
70        // Test that 0 is rejected
71        let toml_str = r#"
72            spaces-per-tab = 0
73        "#;
74        let result: Result<MD010Config, _> = toml::from_str(toml_str);
75        assert!(result.is_err());
76        let err = result.unwrap_err().to_string();
77        assert!(err.contains("must be at least 1") || err.contains("got 0"));
78    }
79
80    #[test]
81    fn test_code_blocks_defaults_false() {
82        let config = MD010Config::default();
83        assert!(!config.code_blocks, "MD010 defaults to skipping code blocks");
84    }
85
86    #[test]
87    fn test_code_blocks_kebab_case() {
88        let config: MD010Config = toml::from_str("code-blocks = true\n").unwrap();
89        assert!(config.code_blocks);
90        assert_eq!(config.spaces_per_tab.get(), 4);
91    }
92
93    #[test]
94    fn test_code_blocks_snake_case_alias() {
95        let config: MD010Config = toml::from_str("code_blocks = true\n").unwrap();
96        assert!(config.code_blocks);
97    }
98}