1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use std::mem;

#[derive(Debug, Clone, PartialEq)]
pub struct FormattedMessageDetail {
    raw: String,
    components: Vec<FormattedMessageComponent>,
}

impl FormattedMessageDetail {
    pub fn new(raw: String, components: Vec<FormattedMessageComponent>) -> Self {
        Self {
            raw,
            components,
        }
    }

    pub fn raw(&self) -> &str {
        &self.raw
    }

    pub fn components(&self) -> &Vec<FormattedMessageComponent> {
        &self.components
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum FormattedMessageComponent {
    Section(String, Vec<FormattedString>),
    Text(Vec<FormattedString>),
}

#[derive(Debug, Clone, PartialEq)]
pub struct FormattedString {
    styles: Vec<Style>,
    s: String,
}

impl FormattedString {
    pub fn new<S: ToString>(s: S, styles: Vec<Style>) -> Self {
        Self {
            s: s.to_string(),
            styles
        }
    }

    pub fn plain<S: ToString>(s: S) -> Self {
        Self::new(s, vec![])
    }

    pub fn styled<S: ToString>(s: S, style: Style) -> Self {
        Self::new(s, vec![style])
    }

    pub fn get_styles(&self) -> &Vec<Style> {
        &self.styles
    }

    pub fn get_string(&self) -> &str {
        &self.s
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Style {
    Bold,
    Italics,
    Monospace,
    Code{ lang: String }
}

pub fn parse_raw_to_formatted(s: &str) -> FormattedMessageDetail {
    let mut components = vec![];

    let mut section_title = None;
    let mut section_text = vec![];

    fn push_section(section_title: &mut Option<String>, section_text: &mut Vec<FormattedString>, components: &mut Vec<FormattedMessageComponent>) {
        if section_title.is_some() || !section_text.is_empty() {
            let old = mem::replace(section_text, vec![]);
            let component = if let Some(title) = section_title.take() {
                FormattedMessageComponent::Section(title, old)
            } else {
                FormattedMessageComponent::Text(old)
            };
            components.push(component);
        }
    }

    for line in s.lines() {
        // #<section text>#
        if line.len() > 4 && line.starts_with("#<") && line.ends_with(">#") {
            push_section(&mut section_title, &mut section_text, &mut components);
            section_title = Some(line[2..line.len() - 2].to_owned());
        } else {
            section_text.extend_from_slice(&parse_section_text(&format!("{}\n", line)));
        }
    }
    push_section(&mut section_title, &mut section_text, &mut components);
    FormattedMessageDetail::new(s.to_owned(), components)
}

fn parse_section_text(s: &str) -> Vec<FormattedString> {
    vec![FormattedString::new(s.to_owned(), vec![])]
}