rnotifylib/message/
detail_builder.rs

1use crate::message::formatted_detail::{FormattedMessageComponent, FormattedMessageDetail, FormattedString, Style};
2use crate::message::MessageDetail;
3
4pub trait FormattedStringAppendable {
5    fn append(&mut self, s: FormattedString) -> &mut Self;
6
7    fn append_styled<S: ToString>(&mut self, s: S, style: Style) -> &mut Self {
8        self.append(FormattedString::styled(s, style));
9        self
10    }
11
12    fn append_plain<S: ToString>(&mut self, s: S) -> &mut Self {
13        self.append(FormattedString::plain(s));
14        self
15    }
16}
17
18pub struct MessageDetailBuilder {
19    contents: Vec<FormattedMessageComponent>,
20    raw: String,
21}
22
23impl MessageDetailBuilder {
24    pub fn new() -> Self {
25        Self::with_raw(String::from("Raw not available"))
26    }
27
28    pub fn with_raw(raw: String) -> Self {
29        Self {
30            contents: vec![],
31            raw,
32        }
33    }
34
35    pub fn raw(&mut self, raw: String) -> &mut Self {
36        self.raw = raw;
37        self
38    }
39
40    pub fn section<F, S>(&mut self, name: S, apply: F) -> &mut Self
41        where F: FnOnce(&mut SectionBuilder),
42              S: ToString {
43        let mut section = SectionBuilder {
44            name: name.to_string(),
45            contents: vec![],
46        };
47        apply(&mut section);
48        self.contents.push(section.build());
49        self
50    }
51
52    pub fn text_block<F>(&mut self, apply: F) -> &mut Self
53        where F: FnOnce(&mut TextBlockBuilder) {
54        let mut text_block = TextBlockBuilder::default();
55        apply(&mut text_block);
56        self.contents.push(text_block.build());
57        self
58    }
59
60    pub fn build(self) -> MessageDetail {
61        MessageDetail::Formatted(FormattedMessageDetail::new(self.raw, self.contents))
62    }
63}
64
65#[derive(Default)]
66pub struct TextBlockBuilder {
67    contents: Vec<FormattedString>,
68}
69
70impl TextBlockBuilder {
71    pub fn build(self) -> FormattedMessageComponent {
72        FormattedMessageComponent::Text(self.contents)
73    }
74
75    pub fn build_vec(self) -> Vec<FormattedString> {
76        self.contents
77    }
78}
79
80impl FormattedStringAppendable for TextBlockBuilder {
81    fn append(&mut self, s: FormattedString) -> &mut Self {
82        self.contents.push(s);
83        self
84    }
85}
86
87pub struct SectionBuilder {
88    name: String,
89    contents: Vec<FormattedString>,
90}
91
92impl SectionBuilder {
93    pub fn build(self) -> FormattedMessageComponent {
94        FormattedMessageComponent::Section(self.name, self.contents)
95    }
96}
97
98impl FormattedStringAppendable for SectionBuilder {
99    fn append(&mut self, s: FormattedString) -> &mut Self {
100        self.contents.push(s);
101        self
102    }
103}
104
105#[cfg(test)]
106mod test {
107    use crate::message::formatted_detail::{FormattedMessageComponent, FormattedMessageDetail, FormattedString, Style};
108    use crate::message::detail_builder::{FormattedStringAppendable, MessageDetailBuilder};
109    use crate::message::MessageDetail;
110
111    #[test]
112    fn test_detail_builder() {
113        let mut builder = MessageDetailBuilder::new();
114        builder.text_block(|block| {
115                block.append_plain("Base Description");
116            })
117            .section("New section", |section| {
118                section.append_styled("hello", Style::Monospace);
119            });
120
121        let built = builder.build();
122
123        let test = MessageDetail::Formatted(FormattedMessageDetail::new(
124            "Raw not available".to_string(),
125            vec![
126                FormattedMessageComponent::Text(vec![FormattedString::plain("Base Description".to_owned())]),
127                FormattedMessageComponent::Section("New section".to_owned(),
128                                                    vec![FormattedString::styled("hello", Style::Monospace)])]
129        ));
130
131        assert_eq!(built, test);
132    }
133}