markdown_builder/types/
header.rs

1use std::fmt;
2use tousize::ToUsize;
3
4/// The level of a header.
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct HeaderLevel(usize);
7
8impl Default for HeaderLevel {
9    /// Returns the default header level (1).
10    fn default() -> Self {
11        HeaderLevel(1)
12    }
13}
14
15impl HeaderLevel {
16    /// Creates a new default header level of 1.
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Creates a new header level.
22    ///
23    /// Panics if the header level is not valid (one to six inclusive).
24    pub fn from(level: impl ToUsize) -> Self {
25        let level = level.to_usize();
26        assert!((1..=6).contains(&level));
27        Self(level)
28    }
29}
30
31impl<T> From<T> for HeaderLevel
32where
33    T: ToUsize,
34{
35    fn from(value: T) -> Self {
36        Self::from(value)
37    }
38}
39
40/// A markdown header.
41#[derive(Clone, Debug, Default, Eq, PartialEq)]
42pub struct Header {
43    /// The header text.
44    pub text: String,
45    /// The header level.
46    pub level: HeaderLevel,
47}
48
49impl Header {
50    /// Creates a new empty header with a level of 1.
51    pub fn new() -> Self {
52        Self::default()
53    }
54
55    /// Creates a new header.
56    ///
57    /// Panics if the header level is not valid (one to six inclusive).
58    /// Lower level means more important header.
59    pub fn from(text: impl Into<String>, level: impl Into<HeaderLevel>) -> Self {
60        Self {
61            text: text.into(),
62            level: level.into(),
63        }
64    }
65}
66
67impl fmt::Display for Header {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        writeln!(f, "{} {}", "#".repeat(self.level.0), self.text)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::{Header, HeaderLevel};
76    use crate::MarkdownElement;
77
78    #[test]
79    fn test_header_level() {
80        assert_eq!(HeaderLevel::from(5usize), 5usize.into());
81    }
82
83    #[test]
84    #[should_panic]
85    fn test_header_level_panic_lowball() {
86        HeaderLevel::from(0usize);
87    }
88
89    #[test]
90    #[should_panic]
91    fn test_header_level_panic_highball() {
92        HeaderLevel::from(7usize);
93    }
94
95    #[test]
96    fn test_header_of_all_sizes() {
97        assert_eq!(Header::from("A header", 1usize).render(), "# A header\n");
98        assert_eq!(Header::from("A header", 2usize).render(), "## A header\n");
99        assert_eq!(Header::from("A header", 3usize).render(), "### A header\n");
100        assert_eq!(Header::from("A header", 4usize).render(), "#### A header\n");
101        assert_eq!(
102            Header::from("A header", 5usize).render(),
103            "##### A header\n"
104        );
105        assert_eq!(
106            Header::from("A header", 6usize).render(),
107            "###### A header\n"
108        );
109    }
110
111    #[test]
112    fn test_header_default() {
113        assert_eq!(Header::new().level, 1usize.into());
114        assert_eq!(Header::new().text, "");
115    }
116}