#[derive(Debug, Clone)] pub enum ShellType { Unknown, Bash, Zsh, } type ColorMap = std::collections::HashMap; pub struct Colors { color_map: ColorMap, unknown_color: term::color::Color, shell_type: ShellType, } impl ShellType { pub fn from_str(shell: &str) -> Self { match shell { "bash" => ShellType::Bash, "zsh" => ShellType::Zsh, _ => panic!("unknown shell {}", shell), } } } impl Colors { pub fn new(shell_type: ShellType) -> Colors { let mut color_map = std::collections::HashMap::new(); color_map.insert(String::from("user_root"), term::color::BRIGHT_RED); color_map .insert(String::from("path_not_writable"), term::color::YELLOW); color_map.insert(String::from("path_not_exist"), term::color::RED); color_map.insert(String::from("vcs_dirty"), term::color::RED); color_map.insert(String::from("vcs_error"), term::color::BRIGHT_RED); color_map.insert(String::from("battery_warn"), term::color::YELLOW); color_map.insert(String::from("battery_crit"), term::color::RED); color_map .insert(String::from("battery_emerg"), term::color::BRIGHT_RED); color_map.insert(String::from("battery_full"), term::color::GREEN); color_map .insert(String::from("battery_charging"), term::color::GREEN); color_map.insert(String::from("default"), term::color::BRIGHT_BLACK); color_map.insert(String::from("error"), term::color::RED); let unknown_color = term::color::YELLOW; Self::read_colors_from_env(&mut color_map); Colors { color_map, unknown_color, shell_type, } } fn read_colors_from_env(color_map: &mut ColorMap) { if let Ok(val) = std::env::var("FANCY_PROMPT_COLORS") { for mapping in val.split(',') { let parts: Vec<_> = mapping.split('=').collect(); let (name, color) = (parts[0], parts[1]); color_map.insert( String::from(name), Self::color_from_string(color), ); } } } fn color_from_string(color_name: &str) -> term::color::Color { match color_name { "black" => term::color::BLACK, "blue" => term::color::BLUE, "bright_black" => term::color::BRIGHT_BLACK, "bright_blue" => term::color::BRIGHT_BLUE, "bright_cyan" => term::color::BRIGHT_CYAN, "bright_green" => term::color::BRIGHT_GREEN, "bright_magenta" => term::color::BRIGHT_MAGENTA, "bright_red" => term::color::BRIGHT_RED, "bright_white" => term::color::BRIGHT_WHITE, "bright_yellow" => term::color::BRIGHT_YELLOW, "cyan" => term::color::CYAN, "green" => term::color::GREEN, "magenta" => term::color::MAGENTA, "red" => term::color::RED, "white" => term::color::WHITE, "yellow" => term::color::YELLOW, _ => panic!("unknown color {}", color_name), } } pub fn print( &self, t: &mut dyn term::Terminal, color: &str, text: &str, ) { let color = self.color_map.get(color); self.print_with_color(t, color, text); } pub fn pad( &self, t: &mut dyn term::Terminal, len: usize, ) { write!(t, "{}", " ".repeat(len)).unwrap(); } pub fn newline( &self, t: &mut dyn term::Terminal, ) { writeln!(t).unwrap(); } pub fn print_host( &self, t: &mut dyn term::Terminal, host: Option<&str>, text: &str, ) { let color = host.and_then(|hostname| { self.color_map.get(&format!("host_{}", hostname)) }); self.print_with_color(t, color, text); } pub fn print_user( &self, t: &mut dyn term::Terminal, user: Option<&str>, text: &str, ) { let color = user.and_then(|username| { self.color_map.get(&format!("user_{}", username)) }); self.print_with_color(t, color, text); } fn print_with_color( &self, t: &mut dyn term::Terminal, color: Option<&term::color::Color>, text: &str, ) { self.print_color(t, color); write!(t, "{}", text).unwrap(); self.print_reset(t); } fn print_reset( &self, t: &mut dyn term::Terminal, ) { self.print_wrapped(t, |t| { t.reset().unwrap(); }) } fn print_color( &self, t: &mut dyn term::Terminal, color: Option<&term::color::Color>, ) { self.print_wrapped(t, |t| { let real_color = *color.unwrap_or(&self.unknown_color); t.fg(real_color).unwrap(); match real_color { term::color::BRIGHT_BLACK | term::color::BRIGHT_BLUE | term::color::BRIGHT_CYAN | term::color::BRIGHT_GREEN | term::color::BRIGHT_MAGENTA | term::color::BRIGHT_RED | term::color::BRIGHT_WHITE | term::color::BRIGHT_YELLOW => { t.attr(term::Attr::Bold).unwrap() } _ => {} } }) } fn print_wrapped( &self, t: &mut dyn term::Terminal, printer: T, ) where T: FnOnce(&mut dyn term::Terminal), { match self.shell_type { ShellType::Bash => { print!("\\["); } ShellType::Zsh => { print!("%{{"); } _ => {} } printer(t); match self.shell_type { ShellType::Bash => { print!("\\]"); } ShellType::Zsh => { print!("%}}"); } _ => {} } } }