use crate::colors::SectionColors;
use crate::config::Config;
use crate::types::Section;
use crate::utils::RESET;
pub fn render_segments(sections: &[&Section], config: &Config) -> String {
let mut result = String::new();
for (i, section) in sections.iter().enumerate() {
result.push_str(&render_section(§ion.content, §ion.colors, config));
if i < sections.len() - 1 {
let next = sections[i + 1];
if config.display.use_powerline {
let left_bg = section.colors.background.unwrap_or((0, 0, 0));
let right_bg = next.colors.background.unwrap_or((0, 0, 0));
result.push_str(&make_powerline_arrow(left_bg, right_bg, config));
} else if !config.display.segment_separator.is_empty() {
result.push_str(&make_separator(config));
}
}
}
if config.display.use_powerline {
if let Some(last) = sections.last() {
if let Some(bg) = last.colors.background {
result.push_str(&format!(
"\x1b[38;2;{};{};{}m\x1b[49m{}",
bg.0, bg.1, bg.2, config.display.arrow
));
}
}
}
result.push_str(RESET);
result
}
fn render_section(content: &str, colors: &SectionColors, config: &Config) -> String {
let bg_code = match colors.background {
Some(bg) => format!("\x1b[48;2;{};{};{}m", bg.0, bg.1, bg.2),
None => "\x1b[49m".to_string(), };
let padding = " ".repeat(config.display.section_padding);
format!(
"{}\x1b[38;2;{};{};{}m{}{}{}",
bg_code,
colors.foreground.0,
colors.foreground.1,
colors.foreground.2,
padding,
content,
padding
)
}
fn make_powerline_arrow(left_bg: (u8, u8, u8), right_bg: (u8, u8, u8), config: &Config) -> String {
format!(
"\x1b[38;2;{};{};{}m\x1b[48;2;{};{};{}m{}",
left_bg.0, left_bg.1, left_bg.2, right_bg.0, right_bg.1, right_bg.2, config.display.arrow
)
}
fn make_details_from_section(colors: &SectionColors) -> String {
format!(
"\x1b[38;2;{};{};{}m",
colors.details.0, colors.details.1, colors.details.2
)
}
fn restore_fg_from_section(colors: &SectionColors) -> String {
format!(
"\x1b[38;2;{};{};{}m",
colors.foreground.0, colors.foreground.1, colors.foreground.2
)
}
pub fn get_details_and_fg_codes(colors: &SectionColors, _config: &Config) -> (String, String) {
(
make_details_from_section(colors),
restore_fg_from_section(colors),
)
}
fn make_separator(config: &Config) -> String {
let color = config.theme.separator;
format!(
"{}\x1b[38;2;{};{};{}m{}\x1b[0m",
RESET, color.0, color.1, color.2, config.display.segment_separator
)
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_COLORS: SectionColors = SectionColors {
background: Some((100, 100, 100)),
foreground: (200, 200, 200),
details: (150, 150, 150),
};
const TEST_COLORS_NO_BG: SectionColors = SectionColors {
background: None,
foreground: (255, 255, 255),
details: (128, 128, 128),
};
#[test]
fn test_render_segments_empty() {
let config = Config::default();
let sections: Vec<&Section> = vec![];
let result = render_segments(§ions, &config);
assert_eq!(result, RESET);
}
#[test]
fn test_render_segments_single() {
let config = Config::default();
let section = Section::new("test".to_string(), 0, TEST_COLORS);
let result = render_segments(&[§ion], &config);
assert!(result.contains("test"));
assert!(result.ends_with(RESET));
}
#[test]
fn test_render_segments_with_powerline() {
let mut config = Config::default();
config.display.use_powerline = true;
config.display.arrow = "".to_string();
let section1 = Section::new("test1".to_string(), 0, TEST_COLORS);
let section2 = Section::new("test2".to_string(), 1, TEST_COLORS);
let result = render_segments(&[§ion1, §ion2], &config);
assert!(result.contains("test1"));
assert!(result.contains("test2"));
assert!(result.contains(""));
}
#[test]
fn test_render_segments_with_separator() {
let mut config = Config::default();
config.display.use_powerline = false;
config.display.segment_separator = " | ".to_string();
let section1 = Section::new("test1".to_string(), 0, TEST_COLORS);
let section2 = Section::new("test2".to_string(), 1, TEST_COLORS);
let result = render_segments(&[§ion1, §ion2], &config);
assert!(result.contains("|"));
}
#[test]
fn test_make_details_from_section() {
let result = make_details_from_section(&TEST_COLORS);
assert!(result.starts_with("\x1b[38;2;"));
assert!(result.contains("150"));
}
#[test]
fn test_restore_fg_from_section() {
let result = restore_fg_from_section(&TEST_COLORS);
assert!(result.starts_with("\x1b[38;2;"));
assert!(result.contains("200"));
}
#[test]
fn test_get_details_and_fg_codes() {
let config = Config::default();
let (details, fg) = get_details_and_fg_codes(&TEST_COLORS, &config);
assert!(details.starts_with("\x1b["));
assert!(fg.starts_with("\x1b["));
assert_ne!(details, fg);
}
#[test]
fn test_render_section_no_background() {
let config = Config::default();
let result = render_section("test", &TEST_COLORS_NO_BG, &config);
assert!(result.contains("\x1b[49m"));
assert!(result.contains("test"));
}
#[test]
fn test_render_section_with_background() {
let config = Config::default();
let result = render_section("test", &TEST_COLORS, &config);
assert!(result.contains("\x1b[48;2;"));
assert!(result.contains("100"));
}
#[test]
fn test_make_powerline_arrow() {
let mut config = Config::default();
config.display.arrow = "".to_string();
let left = (100, 100, 100);
let right = (200, 200, 200);
let result = make_powerline_arrow(left, right, &config);
assert!(result.contains("100"));
assert!(result.contains("200"));
assert!(result.contains(""));
}
#[test]
fn test_rgb_values_boundary() {
let colors_min = SectionColors {
background: Some((0, 0, 0)),
foreground: (0, 0, 0),
details: (0, 0, 0),
};
let result = make_details_from_section(&colors_min);
assert!(result.contains("0"));
let colors_max = SectionColors {
background: Some((255, 255, 255)),
foreground: (255, 255, 255),
details: (255, 255, 255),
};
let result = make_details_from_section(&colors_max);
assert!(result.contains("255"));
}
}