diff --git a/src/color.rs b/src/color.rs index e0d6fe0..67ba1b6 100644 --- a/src/color.rs +++ b/src/color.rs @@ -7,20 +7,32 @@ use std::fmt; /// Shortcut to produce a String colored with one or more colors. /// Example: /// ``` -/// let s = color!("Red string", Red); -/// let x = color!("Hyperlink-ish", Blue, Underline); -macro_rules! color { +/// let s = color_string!("Red string", Red); +/// let x = color_string!("Hyperlink-ish", Blue, Underline); +macro_rules! color_string { ($s:expr, $( $color:ident ),+) => {{ - use crate::color; - let codes = [$( color::$color.as_ref() ),+]; - if codes.len() > 1 { - format!("\x1b[{}m{}{}", codes.join(";"), $s, color::Reset) - } else { - format!("\x1b[{}m{}{}", codes[0], $s, color::Reset) - } + let mut out = String::from("\x1b["); + $( out.push_str(crate::color::$color::code()); out.push_str(";"); )+ + out.push('m'); + out.push_str(&$s); + out.push_str(crate::color::Reset.as_ref()); + out.replace(";m", "m") }}; } +/// Shortcut to produce a color's ANSI escape code. Don't forget to Reset! +/// ``` +/// let mut o = String::new(); +/// o.push_str(color!(Blue)); +/// o.push_str(color!(Underline)); +/// o.push_str("Hyperlinkish."); +/// o.push_str(color!(Reset)); +macro_rules! color { + ($color:ident) => { + crate::color::$color.as_ref() + }; +} + /// Create a color:: struct that can be used with format!. /// Example: /// ``` @@ -34,13 +46,20 @@ macro_rules! define_color { pub struct $color; impl fmt::Display for $color { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\x1b[{}m", self.as_ref()) + write!(f, "{}", self.as_ref()) + } + } + impl $color { + #[allow(missing_docs)] + #[inline] + pub fn code() -> &'static str { + concat!($code) } } impl AsRef for $color { #[inline] fn as_ref(&self) -> &'static str { - concat!($code) + concat!("\x1b[", $code, "m") } } }; @@ -81,13 +100,13 @@ define_color!(WhiteBG, 47); mod tests { #[test] fn test_colors() { - assert_eq!(color!("Error", Red), "\x1b[91mError\x1b[0m"); + assert_eq!(color_string!("Error", Red), "\x1b[91mError\x1b[0m"); assert_eq!( - color!("Fancy Pants", Blue, Underline), + color_string!("Fancy Pants", Blue, Underline), "\x1b[94;4mFancy Pants\x1b[0m" ); assert_eq!( - color!("Super-duper-fancy-pants", Magenta, Underline, Bold, BlueBG), + color_string!("Super-duper-fancy-pants", Magenta, Underline, Bold, BlueBG), "\x1b[95;4;1;44mSuper-duper-fancy-pants\x1b[0m" ) } diff --git a/src/menu.rs b/src/menu.rs index 7479d1e..18bc9e6 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -213,7 +213,9 @@ impl Menu { out.push_str(" "); } else { if line.link == self.link && self.show_cursor() { - out.push_str(&color!("*", Bold)) + out.push_str(color!(Bold)); + out.push_str("*"); + out.push_str(color!(Reset)); } else { out.push(' '); } @@ -229,23 +231,29 @@ impl Menu { // truncate long lines, instead of wrapping let text = if line.text.len() > MAX_COLS { - line.text.chars().take(MAX_COLS).collect::() + &line.text[..MAX_COLS] } else { - line.text.to_string() + &line.text }; // color the line - out.push_str(&match line.typ { - Type::Text => color!(text, Cyan), - Type::Menu => color!(text, Blue), - Type::Info => color!(text, Yellow), - Type::HTML => color!(text, Green), - Type::Error => color!(text, Red), - Type::Telnet => color!(text, Grey), - typ if typ.is_download() => color!(text, Underline, White), - typ if !typ.is_supported() => color!(text, Red, WhiteBG), - _ => text, - }); + if line.typ.is_download() { + out.push_str(&color_string!(text, Underline, White)); + } else if !line.typ.is_supported() { + out.push_str(&color_string!(text, Red, WhiteBG)); + } else { + out.push_str(&match line.typ { + Type::Text => color!(Cyan), + Type::Menu => color!(Blue), + Type::Info => color!(Yellow), + Type::HTML => color!(Green), + Type::Error => color!(Red), + Type::Telnet => color!(Grey), + _ => color!(Red), + }); + } + out.push_str(&text); + out.push_str(color!(Reset)); // clear rest of line out.push_str(&format!("{}", clear::UntilNewline)); diff --git a/src/ui.rs b/src/ui.rs index eb20db8..633b5d0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -349,14 +349,14 @@ impl UI { fn render_conn_status(&self) -> Option { let page = self.views.get(self.focused)?; if page.is_tls() { - let status = color!("TLS", Black, GreenBG); + let status = color_string!("TLS", Black, GreenBG); return Some(format!( "{}{}", termion::cursor::Goto(self.cols() - 3, self.rows()), if self.config.emoji { "🔐" } else { &status }, )); } else if page.is_tor() { - let status = color!("TOR", Bold, White, MagentaBG); + let status = color_string!("TOR", Bold, White, MagentaBG); return Some(format!( "{}{}", termion::cursor::Goto(self.cols() - 3, self.rows()),