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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::{
    generator::Generator,
    parser::{Item, ItemC},
};

/// Tag of a [Element][crate::parser::Item::Element] after tag conversion stage.
///
/// Fg/Bg/Modifier variant is so-called builtin tag, Custom variant contains custom tag type.
#[derive(Debug, Clone)]
pub enum Tag<'a, C: TagConvertor<'a> + ?Sized> {
    /// Tag for change foreground color.
    Fg(C::Color),
    /// Tag for change background color.
    Bg(C::Color),
    /// Tag for use style modifier.
    Modifier(C::Modifier),
    /// A custom tag.
    Custom(C::Custom),
}

/// Tag type for a generator G.
pub type TagG<'a, G> = Tag<'a, <G as Generator<'a>>::Convertor>;

/// Trait for convert a raw tag string to [`Tag`] type.
///
/// Each generator has it own tag convertor, because different backend(show the final output)
/// supports different kind of tags.
///
/// This Trait has three assoc type:
///
/// - Color: for foreground or background color
/// - Modifier: for style modifier(like bold, italic, etc.)
/// - Custom: for custom tag
///
/// The Generator with this convertor `C` will received a series of Item<[Tag<C>][Tag]>, and convert it into final output.
pub trait TagConvertor<'a> {
    /// Color type for foreground and background typed tag.
    type Color;
    /// Modifier type for modifier typed tag.
    type Modifier;
    /// Custom tag type. Usually is the final type represent a style, can be converted from Color and Modifier.
    type Custom;

    /// Parse string to color type.
    fn parse_color(&mut self, s: &str) -> Option<Self::Color>;

    /// Parse string to modifier type.
    fn parse_modifier(&mut self, s: &str) -> Option<Self::Modifier>;

    /// Parse string to custom type.
    ///
    /// Only if this call fails, a convertor try to parse the raw tag string to a built-in tag.
    /// So the custom tag always have higher priority.
    fn parse_custom_tag(&mut self, s: &str) -> Option<Self::Custom>;

    /// Parse string to a builtin tag type.
    fn parse_built_in_tag(&mut self, s: &str) -> Option<Tag<'a, Self>> {
        let mut ty_value = s.split(':');
        let mut ty = ty_value.next()?;
        let value = ty_value.next().unwrap_or_else(|| {
            let value = ty;
            ty = "";
            value
        });

        if ty_value.next().is_some() {
            return None;
        }

        Some(match ty {
            "fg" => Tag::Fg(self.parse_color(value)?),
            "bg" => Tag::Bg(self.parse_color(value)?),
            "mod" => Tag::Modifier(self.parse_modifier(value)?),
            "" => {
                if let Some(color) = self.parse_color(value) {
                    Tag::Fg(color)
                } else if let Some(modifier) = self.parse_modifier(value) {
                    Tag::Modifier(modifier)
                } else {
                    return None;
                }
            }
            _ => return None,
        })
    }

    /// convert the tag string to [Tag] type
    fn convert_tag(&mut self, s: &'a str) -> Option<Tag<'a, Self>> {
        self.parse_custom_tag(s)
            .map(Tag::Custom)
            .or_else(|| self.parse_built_in_tag(s))
    }

    /// Convert item with raw tag string to item with [Tag] type.
    ///
    /// It will filtered out all tags that fail to parse.
    fn convert_item(&mut self, item: Item<'a>) -> ItemC<'a, Self> {
        match item {
            Item::PlainText(pt) => Item::PlainText(pt),
            Item::Element(spans, items) => {
                let tags = spans
                    .into_iter()
                    .flat_map(|span| self.convert_tag(span.fragment()))
                    .collect();

                let subitems = self.convert_line(items);

                Item::Element(tags, subitems)
            }
        }
    }

    /// Convert a line of items with raw tag string to items with [Tag] type.
    ///
    /// It will filtered out all tags that fail to parse.
    fn convert_line(&mut self, items: Vec<Item<'a>>) -> Vec<ItemC<'a, Self>> {
        items.into_iter().map(|item| self.convert_item(item)).collect()
    }

    /// Convert all item with raw tag string of a ast into items with [Tag] type.
    ///
    /// It will filtered out all tags that fail to parse.
    fn convert_ast(&mut self, ast: Vec<Vec<Item<'a>>>) -> Vec<Vec<ItemC<'a, Self>>> {
        ast.into_iter().map(|line| self.convert_line(line)).collect()
    }
}