1use crate::ansi::{color_to_ansi, emphasis_to_ansi};
9use crate::lexer::{TagType, Token};
10
11pub fn render(tokens: Vec<Token>) -> String {
25 let mut result = String::new();
26 for tok in tokens {
27 match tok {
28 Token::Text(text) => result.push_str(text.as_str()),
29 Token::Tag(tag) => match tag {
30 TagType::Color { color, ground } => {
31 result.push_str(color_to_ansi(&color, ground).as_str())
32 }
33 TagType::Emphasis(emphasis) => {
34 result.push_str(emphasis_to_ansi(&emphasis).as_str())
35 }
36 TagType::Reset => result.push_str("\x1b[0m"),
37 TagType::Prefix(prefix) => result.push_str(prefix.as_str()),
38 },
39 }
40 }
41
42 result
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::ansi::{Color, Ground, NamedColor};
49 use crate::lexer::{EmphasisType, TagType, Token};
50
51 #[test]
53 fn test_render_empty_token_list() {
54 let result = render(vec![]);
55 assert_eq!(result, "");
56 }
57 #[test]
58 fn test_render_plain_text_token() {
59 let result = render(vec![Token::Text("hello".into())]);
60 assert_eq!(result, "hello");
61 }
62 #[test]
63 fn test_render_named_color_tag() {
64 let result = render(vec![Token::Tag(TagType::Color {
65 color: Color::Named(NamedColor::Red),
66 ground: Ground::Foreground,
67 })]);
68 assert_eq!(result, "\x1b[31m");
69 }
70 #[test]
71 fn test_render_emphasis_tag_bold() {
72 let result = render(vec![Token::Tag(TagType::Emphasis(EmphasisType::Bold))]);
73 assert_eq!(result, "\x1b[1m");
74 }
75 #[test]
76 fn test_render_reset_tag() {
77 let result = render(vec![Token::Tag(TagType::Reset)]);
78 assert_eq!(result, "\x1b[0m");
79 }
80 #[test]
81 fn test_render_color_then_text() {
82 let result = render(vec![
83 Token::Tag(TagType::Color {
84 color: Color::Named(NamedColor::Red),
85 ground: Ground::Foreground,
86 }),
87 Token::Text("hello".into()),
88 ]);
89 assert_eq!(result, "\x1b[31mhello");
90 }
91 #[test]
92 fn test_render_color_text_reset() {
93 let result = render(vec![
94 Token::Tag(TagType::Color {
95 color: Color::Named(NamedColor::Green),
96 ground: Ground::Foreground,
97 }),
98 Token::Text("go".into()),
99 Token::Tag(TagType::Reset),
100 ]);
101 assert_eq!(result, "\x1b[32mgo\x1b[0m");
102 }
103 #[test]
104 fn test_render_multiple_text_tokens() {
105 let result = render(vec![Token::Text("foo".into()), Token::Text("bar".into())]);
106 assert_eq!(result, "foobar");
107 }
108 #[test]
109 fn test_render_ansi256_color_tag() {
110 let result = render(vec![Token::Tag(TagType::Color {
111 color: Color::Ansi256(21),
112 ground: Ground::Foreground,
113 })]);
114 assert_eq!(result, "\x1b[38;5;21m");
115 }
116 #[test]
117 fn test_render_rgb_color_tag() {
118 let result = render(vec![Token::Tag(TagType::Color {
119 color: Color::Rgb(255, 0, 0),
120 ground: Ground::Foreground,
121 })]);
122 assert_eq!(result, "\x1b[38;2;255;0;0m");
123 }
124 #[test]
125 fn test_render_does_not_append_trailing_reset() {
126 let result = render(vec![Token::Text("plain".into())]);
127 assert!(!result.ends_with("\x1b[0m"));
128 }
129 #[test]
130 fn test_render_named_color_background() {
131 let result = render(vec![Token::Tag(TagType::Color {
132 color: Color::Named(NamedColor::Red),
133 ground: Ground::Background,
134 })]);
135 assert_eq!(result, "\x1b[41m");
136 }
137 #[test]
138 fn test_render_ansi256_background() {
139 let result = render(vec![Token::Tag(TagType::Color {
140 color: Color::Ansi256(21),
141 ground: Ground::Background,
142 })]);
143 assert_eq!(result, "\x1b[48;5;21m");
144 }
145 #[test]
146 fn test_render_rgb_background() {
147 let result = render(vec![Token::Tag(TagType::Color {
148 color: Color::Rgb(255, 0, 0),
149 ground: Ground::Background,
150 })]);
151 assert_eq!(result, "\x1b[48;2;255;0;0m");
152 }
153 #[test]
154 fn test_render_fg_and_bg_together() {
155 let result = render(vec![
156 Token::Tag(TagType::Color {
157 color: Color::Named(NamedColor::White),
158 ground: Ground::Foreground,
159 }),
160 Token::Tag(TagType::Color {
161 color: Color::Named(NamedColor::Blue),
162 ground: Ground::Background,
163 }),
164 Token::Text("hello".into()),
165 ]);
166 assert_eq!(result, "\x1b[37m\x1b[44mhello");
167 }
168}
169