prompt_color_tool/
lib.rs

1use md5::{Md5, Digest};
2use std::process::Command as ProcessCommand;
3
4pub mod theme_maps;
5
6pub const BGCOLOR_ENV_VAR: &str = "PLGO_HOSTNAMEBG";
7pub const FGCOLOR_ENV_VAR: &str = "PLGO_HOSTNAMEFG";
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Rgb {
11    pub r: u8,
12    pub g: u8,
13    pub b: u8,
14    pub xterm: u8,
15}
16
17impl Rgb {
18    pub fn new(xterm: u8) -> Self {
19        let mut rgb = Rgb { r: 0, g: 0, b: 0, xterm };
20        rgb.to_rgb();
21        rgb
22    }
23
24    pub fn to_hex(self) -> String {
25        format!("{:02x}{:02x}{:02x}", self.r, self.g, self.b)
26    }
27
28    pub fn to_rgb(&mut self) {
29        (self.r, self.g, self.b) = xterm_to_rgb(self.xterm);
30    }
31}
32
33pub fn xterm_to_rgb(xterm_color: u8) -> (u8, u8, u8) {
34    if xterm_color < 16 {
35        let colors = vec![
36            "000000", "cd0000", "00cd00", "cdcd00", "0000ee", "cd00cd", "00cdcd", "e5e5e5",
37            "7f7f7f", "ff0000", "00ff00", "ffff00", "5c5cff", "ff00ff", "00ffff", "ffffff",
38        ];
39
40        let color = colors[xterm_color as usize];
41        let r = u8::from_str_radix(&color[0..2], 16).unwrap();
42        let g = u8::from_str_radix(&color[2..4], 16).unwrap();
43        let b = u8::from_str_radix(&color[4..6], 16).unwrap();
44
45        return  (r, g, b);
46    }
47
48    if xterm_color < 232 {
49        let idx = xterm_color - 16;
50        let red = idx / 36;
51        let green = (idx % 36) / 6;
52        let blue = idx % 6;
53
54        let values = vec![0, 95, 135, 175, 215, 255];
55        let r_color = values[red as usize];
56        let g_color = values[green as usize];
57        let b_color = values[blue as usize];
58
59        return (r_color, g_color, b_color);
60    }
61
62    let gray = 8 + (xterm_color - 232) * 10;
63    return (gray, gray, gray);
64}
65
66pub fn calculate_hostname_background_color(hostname: Option<&str>) -> Rgb {
67    let hn = match hostname {
68        Some(h) => h.to_string(),
69        None => {
70            let output = ProcessCommand::new("hostname")
71                .output()
72                .expect("Failed to execute hostname command");
73
74            String::from_utf8_lossy(&output.stdout)
75                .split('.')
76                .next()
77                .unwrap_or("localhost")
78                .trim()
79                .to_string()
80        }
81    };
82
83    let mut hasher = Md5::new();
84    hasher.update(hn.as_bytes());
85    let hash = hasher.finalize();
86
87    let hex_str = format!("{:x}", hash);
88    let first_two_chars = &hex_str[..2];
89    let color_value = u8::from_str_radix(first_two_chars, 16).unwrap() % 128;
90
91    return Rgb::new(color_value);
92}
93
94pub fn calculate_hostname_foreground_color(bgcolor: Rgb, theme: Option<&str>) -> Result<Rgb, String> {
95    let theme = theme.unwrap_or("default");
96
97    if !theme_maps::THEME_MAP.contains_key(theme) {
98        eprintln!("Warning: Theme '{}' not found. Using default theme.", theme);
99    }
100
101    if let Some(map) = theme_maps::THEME_MAP.get(theme).or_else(|| theme_maps::THEME_MAP.get("default")) {
102        if let Some(color) = map.get(&bgcolor.xterm) {
103            return Ok(Rgb::new(*color));
104        }
105        return Err(format!("Color not found for color: {} theme: {} ", bgcolor.xterm, theme));
106    }
107    return Err(format!("Key not found for theme: {}", theme));
108}
109
110pub fn get_color_from_env(varname: &str) -> Result<Rgb, String> {
111    let env_color = std::env::var(varname).ok();
112    if let Some(bgcolor) = env_color {
113        let xterm_color = match bgcolor.parse::<u8>() {
114            Ok(color) => color,
115            Err(_) => return Err(format!("Unable to parse value of environment variable {}={}", varname, bgcolor)),
116        };
117        return Ok(Rgb::new(xterm_color));
118    }
119    return Err(format!("Environment variable {} is not set", varname));
120}
121
122pub fn output_colors_to_string(bgcolor: Rgb, fgcolor: Rgb, verbose: bool, fgonly: bool, bgonly: bool, iterm: bool, hex: bool) -> String {
123    let (bgcolor_output, fgcolor_output) = if hex {
124        (bgcolor.to_hex(), fgcolor.to_hex())
125    } else {
126        (bgcolor.xterm.to_string(), fgcolor.xterm.to_string())
127    };
128
129    if verbose {
130        return format!("bgcolor: {}, fgcolor: {}", bgcolor_output, fgcolor_output);
131    } else if fgonly {
132        return fgcolor_output;
133    } else if bgonly {
134        return bgcolor_output;
135    } else if iterm {
136        let bgcolor_hex = bgcolor.to_hex();
137        let red = u8::from_str_radix(&bgcolor_hex[0..2], 16).unwrap();
138        let green = u8::from_str_radix(&bgcolor_hex[2..4], 16).unwrap();
139        let blue = u8::from_str_radix(&bgcolor_hex[4..6], 16).unwrap();
140
141        return format!(
142            "\x1b]6;1;bg;red;brightness;{}\x07\x1b]6;1;bg;green;brightness;{}\x07\x1b]6;1;bg;blue;brightness;{}\x07",
143            red, green, blue
144        );
145    } else {
146        return format!("{} {}", bgcolor_output, fgcolor_output);
147    }
148}