Skip to main content

rumdl_lib/types/
br_spaces.rs

1use serde::{Deserialize, Serialize};
2
3/// Number of trailing spaces that MD009 accepts as a hard line break.
4///
5/// CommonMark renders a hard line break for two or more trailing spaces, so a
6/// value below 2 cannot describe one. Such a value turns the exception off
7/// instead: every trailing space is then reported. This mirrors markdownlint,
8/// where `br_spaces` of 0 or 1 "disallows any trailing spaces".
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct BrSpaces(usize);
12
13impl BrSpaces {
14    /// Fewest trailing spaces that render a hard line break (CommonMark).
15    pub const MIN: usize = 2;
16
17    pub const fn new(value: usize) -> Self {
18        Self(value)
19    }
20
21    /// The configured value as written.
22    pub fn get(self) -> usize {
23        self.0
24    }
25
26    /// The trailing-space count kept as a hard line break, or `None` when the
27    /// exception is off and every trailing space is reported.
28    pub fn line_break(self) -> Option<usize> {
29        (self.0 >= Self::MIN).then_some(self.0)
30    }
31}
32
33impl Default for BrSpaces {
34    fn default() -> Self {
35        Self(Self::MIN)
36    }
37}
38
39impl From<BrSpaces> for usize {
40    fn from(val: BrSpaces) -> Self {
41        val.0
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn test_line_break_values() {
51        for value in [2, 3, 4, 10, 100] {
52            let br_spaces = BrSpaces::new(value);
53            assert_eq!(br_spaces.get(), value);
54            assert_eq!(usize::from(br_spaces), value);
55            assert_eq!(br_spaces.line_break(), Some(value));
56        }
57    }
58
59    #[test]
60    fn test_values_below_two_turn_the_exception_off() {
61        for value in [0, 1] {
62            let br_spaces = BrSpaces::new(value);
63            assert_eq!(br_spaces.get(), value);
64            assert_eq!(br_spaces.line_break(), None);
65        }
66    }
67
68    #[test]
69    fn test_default() {
70        assert_eq!(BrSpaces::default().get(), 2);
71        assert_eq!(BrSpaces::default().line_break(), Some(2));
72    }
73
74    #[test]
75    fn test_roundtrip() {
76        #[derive(serde::Serialize, serde::Deserialize)]
77        struct TestConfig {
78            spaces: BrSpaces,
79        }
80
81        for value in [0, 1, 2, 3] {
82            let config = TestConfig {
83                spaces: BrSpaces::new(value),
84            };
85            let serialized = toml::to_string(&config).unwrap();
86            assert_eq!(serialized, format!("spaces = {value}\n"));
87            let deserialized: TestConfig = toml::from_str(&serialized).unwrap();
88            assert_eq!(deserialized.spaces.get(), value);
89        }
90    }
91}