1use crate::colors::SectionColors;
2use crate::config::Config;
3use crate::types::Section;
4use crate::utils::RESET;
5
6pub fn render_segments(sections: &[&Section], config: &Config) -> String {
8 let mut result = String::new();
9
10 for (i, section) in sections.iter().enumerate() {
11 result.push_str(&render_section(§ion.content, §ion.colors, config));
12
13 if i < sections.len() - 1 {
14 let next = sections[i + 1];
15 if config.display.use_powerline {
16 let left_bg = section.colors.background.unwrap_or((0, 0, 0));
17 let right_bg = next.colors.background.unwrap_or((0, 0, 0));
18 result.push_str(&make_powerline_arrow(left_bg, right_bg, config));
19 } else if !config.display.segment_separator.is_empty() {
20 result.push_str(&make_separator(config));
21 }
22 }
23 }
24
25 if config.display.use_powerline {
27 if let Some(last) = sections.last() {
28 if let Some(bg) = last.colors.background {
29 result.push_str(&format!(
30 "\x1b[38;2;{};{};{}m\x1b[49m{}",
31 bg.0, bg.1, bg.2, config.display.arrow
32 ));
33 }
34 }
35 }
36
37 result.push_str(RESET);
38 result
39}
40
41fn render_section(content: &str, colors: &SectionColors, config: &Config) -> String {
43 let bg_code = match colors.background {
44 Some(bg) => format!("\x1b[48;2;{};{};{}m", bg.0, bg.1, bg.2),
45 None => "\x1b[49m".to_string(), };
47
48 let padding = " ".repeat(config.display.section_padding);
49
50 format!(
51 "{}\x1b[38;2;{};{};{}m{}{}{}",
52 bg_code,
53 colors.foreground.0,
54 colors.foreground.1,
55 colors.foreground.2,
56 padding,
57 content,
58 padding
59 )
60}
61
62fn make_powerline_arrow(left_bg: (u8, u8, u8), right_bg: (u8, u8, u8), config: &Config) -> String {
64 format!(
65 "\x1b[38;2;{};{};{}m\x1b[48;2;{};{};{}m{}",
66 left_bg.0, left_bg.1, left_bg.2, right_bg.0, right_bg.1, right_bg.2, config.display.arrow
67 )
68}
69
70fn make_details_from_section(colors: &SectionColors) -> String {
72 format!(
73 "\x1b[38;2;{};{};{}m",
74 colors.details.0, colors.details.1, colors.details.2
75 )
76}
77
78fn restore_fg_from_section(colors: &SectionColors) -> String {
80 format!(
81 "\x1b[38;2;{};{};{}m",
82 colors.foreground.0, colors.foreground.1, colors.foreground.2
83 )
84}
85
86pub fn get_details_and_fg_codes(colors: &SectionColors) -> (String, String) {
88 (
89 make_details_from_section(colors),
90 restore_fg_from_section(colors),
91 )
92}
93
94fn make_separator(config: &Config) -> String {
96 let color = config.theme.separator;
97 format!(
98 "{}\x1b[38;2;{};{};{}m{}\x1b[0m",
99 RESET, color.0, color.1, color.2, config.display.segment_separator
100 )
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 const TEST_COLORS: SectionColors = SectionColors {
108 background: Some((100, 100, 100)),
109 foreground: (200, 200, 200),
110 details: (150, 150, 150),
111 };
112
113 const TEST_COLORS_NO_BG: SectionColors = SectionColors {
114 background: None,
115 foreground: (255, 255, 255),
116 details: (128, 128, 128),
117 };
118
119 #[test]
120 fn test_render_segments_empty() {
121 let config = Config::default();
122 let sections: Vec<&Section> = vec![];
123 let result = render_segments(§ions, &config);
124 assert_eq!(result, RESET);
125 }
126
127 #[test]
128 fn test_render_segments_single() {
129 let config = Config::default();
130 let section = Section::new("test".to_string(), 0, TEST_COLORS);
131 let result = render_segments(&[§ion], &config);
132
133 assert!(result.contains("test"));
135 assert!(result.ends_with(RESET));
137 }
138
139 #[test]
140 fn test_render_segments_with_powerline() {
141 let mut config = Config::default();
142 config.display.use_powerline = true;
143 config.display.arrow = "".to_string();
144
145 let section1 = Section::new("test1".to_string(), 0, TEST_COLORS);
146 let section2 = Section::new("test2".to_string(), 1, TEST_COLORS);
147
148 let result = render_segments(&[§ion1, §ion2], &config);
149
150 assert!(result.contains("test1"));
152 assert!(result.contains("test2"));
153 assert!(result.contains(""));
155 }
156
157 #[test]
158 fn test_render_segments_with_separator() {
159 let mut config = Config::default();
160 config.display.use_powerline = false;
161 config.display.segment_separator = " | ".to_string();
162
163 let section1 = Section::new("test1".to_string(), 0, TEST_COLORS);
164 let section2 = Section::new("test2".to_string(), 1, TEST_COLORS);
165
166 let result = render_segments(&[§ion1, §ion2], &config);
167
168 assert!(result.contains("|"));
170 }
171
172 #[test]
173 fn test_make_details_from_section() {
174 let result = make_details_from_section(&TEST_COLORS);
175 assert!(result.starts_with("\x1b[38;2;"));
177 assert!(result.contains("150"));
178 }
179
180 #[test]
181 fn test_restore_fg_from_section() {
182 let result = restore_fg_from_section(&TEST_COLORS);
183 assert!(result.starts_with("\x1b[38;2;"));
185 assert!(result.contains("200"));
186 }
187
188 #[test]
189 fn test_get_details_and_fg_codes() {
190 let (details, fg) = get_details_and_fg_codes(&TEST_COLORS);
191
192 assert!(details.starts_with("\x1b["));
193 assert!(fg.starts_with("\x1b["));
194 assert_ne!(details, fg);
195 }
196
197 #[test]
198 fn test_render_section_no_background() {
199 let config = Config::default();
200 let result = render_section("test", &TEST_COLORS_NO_BG, &config);
201
202 assert!(result.contains("\x1b[49m"));
204 assert!(result.contains("test"));
206 }
207
208 #[test]
209 fn test_render_section_with_background() {
210 let config = Config::default();
211 let result = render_section("test", &TEST_COLORS, &config);
212
213 assert!(result.contains("\x1b[48;2;"));
215 assert!(result.contains("100"));
217 }
218
219 #[test]
220 fn test_make_powerline_arrow() {
221 let mut config = Config::default();
222 config.display.arrow = "".to_string();
223
224 let left = (100, 100, 100);
225 let right = (200, 200, 200);
226 let result = make_powerline_arrow(left, right, &config);
227
228 assert!(result.contains("100"));
230 assert!(result.contains("200"));
232 assert!(result.contains(""));
234 }
235
236 #[test]
237 fn test_rgb_values_boundary() {
238 let colors_min = SectionColors {
240 background: Some((0, 0, 0)),
241 foreground: (0, 0, 0),
242 details: (0, 0, 0),
243 };
244 let result = make_details_from_section(&colors_min);
245 assert!(result.contains("0"));
246
247 let colors_max = SectionColors {
249 background: Some((255, 255, 255)),
250 foreground: (255, 255, 255),
251 details: (255, 255, 255),
252 };
253 let result = make_details_from_section(&colors_max);
254 assert!(result.contains("255"));
255 }
256}