use crate::rule_config_serde::RuleConfig;
use crate::types::HeadingLevel;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum HeadingStyle {
#[default]
Atx,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct MD036Config {
#[serde(default = "default_punctuation")]
pub punctuation: String,
#[serde(default)]
pub fix: bool,
#[serde(default, rename = "heading-style", alias = "heading_style")]
pub heading_style: HeadingStyle,
#[serde(default = "default_heading_level", rename = "heading-level", alias = "heading_level")]
pub heading_level: HeadingLevel,
}
fn default_punctuation() -> String {
".,;:!?".to_string()
}
fn default_heading_level() -> HeadingLevel {
HeadingLevel::new(2).unwrap()
}
impl Default for MD036Config {
fn default() -> Self {
Self {
punctuation: default_punctuation(),
fix: false,
heading_style: HeadingStyle::default(),
heading_level: default_heading_level(),
}
}
}
impl RuleConfig for MD036Config {
const RULE_NAME: &'static str = "MD036";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_values() {
let config = MD036Config::default();
assert_eq!(config.punctuation, ".,;:!?");
assert!(!config.fix);
assert_eq!(config.heading_style, HeadingStyle::Atx);
assert_eq!(config.heading_level.get(), 2);
}
#[test]
fn test_kebab_case_config() {
let toml_str = r#"
punctuation = ".,;:"
fix = true
heading-style = "atx"
heading-level = 3
"#;
let config: MD036Config = toml::from_str(toml_str).unwrap();
assert_eq!(config.punctuation, ".,;:");
assert!(config.fix);
assert_eq!(config.heading_style, HeadingStyle::Atx);
assert_eq!(config.heading_level.get(), 3);
}
#[test]
fn test_snake_case_backwards_compatibility() {
let toml_str = r#"
punctuation = "."
fix = true
heading_style = "atx"
heading_level = 4
"#;
let config: MD036Config = toml::from_str(toml_str).unwrap();
assert_eq!(config.punctuation, ".");
assert!(config.fix);
assert_eq!(config.heading_style, HeadingStyle::Atx);
assert_eq!(config.heading_level.get(), 4);
}
#[test]
fn test_invalid_heading_level_rejected() {
let toml_str = r#"
heading-level = 0
"#;
let result: Result<MD036Config, _> = toml::from_str(toml_str);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("must be between 1 and 6"));
let toml_str = r#"
heading-level = 7
"#;
let result: Result<MD036Config, _> = toml::from_str(toml_str);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("must be between 1 and 6"));
}
#[test]
fn test_all_valid_heading_levels() {
for level in 1..=6 {
let toml_str = format!("heading-level = {level}");
let config: MD036Config = toml::from_str(&toml_str).unwrap();
assert_eq!(config.heading_level.get(), level);
}
}
}