Skip to main content

tui_markup/generator/
tag.rs

1use crate::{
2    generator::Generator,
3    parser::{Item, ItemC},
4};
5
6/// Tag of a [Element][crate::parser::Item::Element] after tag conversion stage.
7///
8/// Fg/Bg/Modifier variant is so-called builtin tag, Custom variant contains custom tag type.
9#[derive(Debug, Clone)]
10pub enum Tag<'a, C: TagConvertor<'a> + ?Sized> {
11    /// Tag for change foreground color.
12    Fg(C::Color),
13    /// Tag for change background color.
14    Bg(C::Color),
15    /// Tag for use style modifier.
16    Modifier(C::Modifier),
17    /// A custom tag.
18    Custom(C::Custom),
19}
20
21/// Tag type for a generator G.
22pub type TagG<'a, G> = Tag<'a, <G as Generator<'a>>::Convertor>;
23
24/// Trait for convert a raw tag string to [`Tag`] type.
25///
26/// Each generator has it own tag convertor, because different backend(show the final output)
27/// supports different kind of tags.
28///
29/// This Trait has three assoc type:
30///
31/// - Color: for foreground or background color
32/// - Modifier: for style modifier(like bold, italic, etc.)
33/// - Custom: for custom tag
34///
35/// The Generator with this convertor `C` will received a series of
36/// [`Item`]&lt;[`Tag`]&lt;C&gt;&gt;, and convert it into final output.
37pub trait TagConvertor<'a> {
38    /// Color type for foreground and background typed tag.
39    type Color;
40    /// Modifier type for modifier typed tag.
41    type Modifier;
42    /// Custom tag type. Usually is the final type represent a style, can be converted from Color
43    /// and Modifier.
44    type Custom;
45
46    /// Parse string to color type.
47    fn parse_color(&mut self, s: &str) -> Option<Self::Color>;
48
49    /// Parse string to modifier type.
50    fn parse_modifier(&mut self, s: &str) -> Option<Self::Modifier>;
51
52    /// Parse string to custom type.
53    ///
54    /// Only if this call fails, a convertor try to parse the raw tag string to a built-in tag.
55    /// So the custom tag always have higher priority.
56    fn parse_custom_tag(&mut self, s: &str) -> Option<Self::Custom>;
57
58    /// Parse string to a builtin tag type.
59    fn parse_built_in_tag(&mut self, s: &str) -> Option<Tag<'a, Self>> {
60        let mut ty_value = s.split(':');
61        let mut ty = ty_value.next()?;
62        let value = ty_value.next().unwrap_or_else(|| {
63            let value = ty;
64            ty = "";
65            value
66        });
67
68        if ty_value.next().is_some() {
69            return None;
70        }
71
72        Some(match ty {
73            "fg" => Tag::Fg(self.parse_color(value)?),
74            "bg" => Tag::Bg(self.parse_color(value)?),
75            "mod" => Tag::Modifier(self.parse_modifier(value)?),
76            "" => {
77                if let Some(color) = self.parse_color(value) {
78                    Tag::Fg(color)
79                } else if let Some(modifier) = self.parse_modifier(value) {
80                    Tag::Modifier(modifier)
81                } else {
82                    return None;
83                }
84            }
85            _ => return None,
86        })
87    }
88
89    /// convert the tag string to [Tag] type
90    fn convert_tag(&mut self, s: &'a str) -> Option<Tag<'a, Self>> {
91        self.parse_custom_tag(s)
92            .map(Tag::Custom)
93            .or_else(|| self.parse_built_in_tag(s))
94    }
95
96    /// Convert item with raw tag string to item with [Tag] type.
97    ///
98    /// It will filtered out all tags that fail to parse.
99    fn convert_item(&mut self, item: Item<'a>) -> ItemC<'a, Self> {
100        match item {
101            Item::PlainText(pt) => Item::PlainText(pt),
102            Item::Element(spans, items) => {
103                let tags = spans
104                    .into_iter()
105                    .filter_map(|span| self.convert_tag(span))
106                    .collect();
107
108                let subitems = self.convert_line(items);
109
110                Item::Element(tags, subitems)
111            }
112        }
113    }
114
115    /// Convert a line of items with raw tag string to items with [Tag] type.
116    ///
117    /// It will filtered out all tags that fail to parse.
118    fn convert_line(&mut self, items: Vec<Item<'a>>) -> Vec<ItemC<'a, Self>> {
119        items
120            .into_iter()
121            .map(|item| self.convert_item(item))
122            .collect()
123    }
124
125    /// Convert all item with raw tag string of a ast into items with [Tag] type.
126    ///
127    /// It will filtered out all tags that fail to parse.
128    fn convert_ast(&mut self, ast: Vec<Vec<Item<'a>>>) -> Vec<Vec<ItemC<'a, Self>>> {
129        ast.into_iter()
130            .map(|line| self.convert_line(line))
131            .collect()
132    }
133}