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`]<[`Tag`]<C>>, 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}