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 [`Item`]&lt;[`Tag`]&lt;C&gt;&gt;, and convert it into final output.
36pub trait TagConvertor<'a> {
37    /// Color type for foreground and background typed tag.
38    type Color;
39    /// Modifier type for modifier typed tag.
40    type Modifier;
41    /// Custom tag type. Usually is the final type represent a style, can be converted from Color and Modifier.
42    type Custom;
43
44    /// Parse string to color type.
45    fn parse_color(&mut self, s: &str) -> Option<Self::Color>;
46
47    /// Parse string to modifier type.
48    fn parse_modifier(&mut self, s: &str) -> Option<Self::Modifier>;
49
50    /// Parse string to custom type.
51    ///
52    /// Only if this call fails, a convertor try to parse the raw tag string to a built-in tag.
53    /// So the custom tag always have higher priority.
54    fn parse_custom_tag(&mut self, s: &str) -> Option<Self::Custom>;
55
56    /// Parse string to a builtin tag type.
57    fn parse_built_in_tag(&mut self, s: &str) -> Option<Tag<'a, Self>> {
58        let mut ty_value = s.split(':');
59        let mut ty = ty_value.next()?;
60        let value = ty_value.next().unwrap_or_else(|| {
61            let value = ty;
62            ty = "";
63            value
64        });
65
66        if ty_value.next().is_some() {
67            return None;
68        }
69
70        Some(match ty {
71            "fg" => Tag::Fg(self.parse_color(value)?),
72            "bg" => Tag::Bg(self.parse_color(value)?),
73            "mod" => Tag::Modifier(self.parse_modifier(value)?),
74            "" => {
75                if let Some(color) = self.parse_color(value) {
76                    Tag::Fg(color)
77                } else if let Some(modifier) = self.parse_modifier(value) {
78                    Tag::Modifier(modifier)
79                } else {
80                    return None;
81                }
82            }
83            _ => return None,
84        })
85    }
86
87    /// convert the tag string to [Tag] type
88    fn convert_tag(&mut self, s: &'a str) -> Option<Tag<'a, Self>> {
89        self.parse_custom_tag(s)
90            .map(Tag::Custom)
91            .or_else(|| self.parse_built_in_tag(s))
92    }
93
94    /// Convert item with raw tag string to item with [Tag] type.
95    ///
96    /// It will filtered out all tags that fail to parse.
97    fn convert_item(&mut self, item: Item<'a>) -> ItemC<'a, Self> {
98        match item {
99            Item::PlainText(pt) => Item::PlainText(pt),
100            Item::Element(spans, items) => {
101                let tags = spans
102                    .into_iter()
103                    .filter_map(|span| self.convert_tag(span.fragment()))
104                    .collect();
105
106                let subitems = self.convert_line(items);
107
108                Item::Element(tags, subitems)
109            }
110        }
111    }
112
113    /// Convert a line of items with raw tag string to items with [Tag] type.
114    ///
115    /// It will filtered out all tags that fail to parse.
116    fn convert_line(&mut self, items: Vec<Item<'a>>) -> Vec<ItemC<'a, Self>> {
117        items.into_iter().map(|item| self.convert_item(item)).collect()
118    }
119
120    /// Convert all item with raw tag string of a ast into items with [Tag] type.
121    ///
122    /// It will filtered out all tags that fail to parse.
123    fn convert_ast(&mut self, ast: Vec<Vec<Item<'a>>>) -> Vec<Vec<ItemC<'a, Self>>> {
124        ast.into_iter().map(|line| self.convert_line(line)).collect()
125    }
126}