Skip to main content

hblank_core/
docs.rs

1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2pub enum CalloutTone {
3    Note,
4    Success,
5    Warning,
6}
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum DocBlock {
10    Heading {
11        level: u8,
12        text: String,
13    },
14    Prose(String),
15    Fixture {
16        id: String,
17    },
18    Props,
19    Controls,
20    Source,
21    Callout {
22        tone: CalloutTone,
23        title: String,
24        body: String,
25    },
26    Custom {
27        id: String,
28        payload: String,
29    },
30}
31
32impl DocBlock {
33    #[must_use]
34    pub fn heading(level: u8, text: impl Into<String>) -> Self {
35        Self::Heading {
36            level: level.clamp(1, 3),
37            text: text.into(),
38        }
39    }
40
41    #[must_use]
42    pub fn prose(text: impl Into<String>) -> Self {
43        Self::Prose(text.into())
44    }
45
46    #[must_use]
47    pub fn fixture(id: impl Into<String>) -> Self {
48        Self::Fixture { id: id.into() }
49    }
50
51    #[must_use]
52    pub const fn props() -> Self {
53        Self::Props
54    }
55
56    #[must_use]
57    pub const fn controls() -> Self {
58        Self::Controls
59    }
60
61    #[must_use]
62    pub const fn source() -> Self {
63        Self::Source
64    }
65
66    #[must_use]
67    pub fn custom(id: impl Into<String>, payload: impl Into<String>) -> Self {
68        Self::Custom {
69            id: id.into(),
70            payload: payload.into(),
71        }
72    }
73
74    #[must_use]
75    pub fn callout(tone: CalloutTone, title: impl Into<String>, body: impl Into<String>) -> Self {
76        Self::Callout {
77            tone,
78            title: title.into(),
79            body: body.into(),
80        }
81    }
82}
83
84#[derive(Clone, Debug, Default, PartialEq, Eq)]
85pub struct DocPage {
86    blocks: Vec<DocBlock>,
87}
88
89impl DocPage {
90    #[must_use]
91    pub fn new(blocks: impl IntoIterator<Item = DocBlock>) -> Self {
92        Self {
93            blocks: blocks.into_iter().collect(),
94        }
95    }
96
97    #[must_use]
98    pub fn blocks(&self) -> &[DocBlock] {
99        &self.blocks
100    }
101
102    #[must_use]
103    pub fn is_empty(&self) -> bool {
104        self.blocks.is_empty()
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn composes_typed_document_blocks_in_order() {
114        let page = DocPage::new([
115            DocBlock::heading(1, "Button"),
116            DocBlock::prose("Use for primary actions."),
117            DocBlock::props(),
118            DocBlock::controls(),
119            DocBlock::source(),
120            DocBlock::custom("project::tokens", "spacing"),
121        ]);
122
123        assert_eq!(page.blocks().len(), 6);
124        assert_eq!(
125            page.blocks()[0],
126            DocBlock::Heading {
127                level: 1,
128                text: "Button".to_owned(),
129            }
130        );
131    }
132}