From 5e04d06c9c78bfb54118a838f74d015b284162f5 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Wed, 25 Sep 2019 21:45:48 +0200 Subject: [PATCH 01/21] moved book doc to lib.rs, fixed comment examples, changed some names, improved documentation --- README.md | 10 +- src/ansi_color.rs | 106 ------ src/color.rs | 170 +-------- src/color/ansi.rs | 148 ++++++++ src/{winapi_color.rs => color/winapi.rs} | 114 +++--- src/enums.rs | 4 +- src/enums/attribute.rs | 18 +- src/enums/color.rs | 111 ------ src/enums/color_type.rs | 111 ++++++ src/enums/colored.rs | 30 +- src/lib.rs | 452 ++++++++++++++++++++--- src/objectstyle.rs | 41 +- src/styledobject.rs | 115 +++--- src/traits.rs | 14 +- 14 files changed, 877 insertions(+), 567 deletions(-) delete mode 100644 src/ansi_color.rs create mode 100644 src/color/ansi.rs rename src/{winapi_color.rs => color/winapi.rs} (62%) delete mode 100644 src/enums/color.rs create mode 100644 src/enums/color_type.rs diff --git a/README.md b/README.md index 19da041..b28c6ec 100644 --- a/README.md +++ b/README.md @@ -91,29 +91,29 @@ _style text with colors_ ```rust use crossterm_style::{Colored, Color, Colorize}; -println!("{} Red foreground color", Colored::Fg(Color::Red)); -println!("{} Blue background color", Colored::Bg(Color::Blue)); +println!("{} Red foreground color", Colored::Fg(ColorType::Red)); +println!("{} Blue background color", Colored::Bg(ColorType::Blue)); // you can also call different coloring methods on a `&str`. let styled_text = "Bold Underlined".red().on_blue(); println!("{}", styled_text); // old-way but still usable -let styled_text = style("Bold Underlined").with(Color::Red).on(Color::Blue); +let styled_text = style("Bold Underlined").with(ColorType::Red).on(ColorType::Blue); ``` _style text with RGB and ANSI Value_ ```rust // custom rgb value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(Color::Rgb { +println!("{} some colored text", Colored::Fg(ColorType::Rgb { r: 10, g: 10, b: 10 })); // custom ansi color value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); +println!("{} some colored text", Colored::Fg(ColorType::AnsiValue(10))); ``` ## Tested terminals diff --git a/src/ansi_color.rs b/src/ansi_color.rs deleted file mode 100644 index 65bda2a..0000000 --- a/src/ansi_color.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! This is a ANSI specific implementation for styling related action. -//! This module is used for Windows 10 terminals and Unix terminals by default. - -use crossterm_utils::{csi, write_cout, Result}; - -use crate::{Attribute, Color, Colored, ITerminalColor}; - -pub fn get_set_fg_ansi(fg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Fg(fg_color)),) -} - -pub fn get_set_bg_ansi(bg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Bg(bg_color)),) -} - -pub fn get_set_attr_ansi(attribute: Attribute) -> String { - format!(csi!("{}m"), attribute as i16,) -} - -pub static RESET_ANSI: &'static str = csi!("0m"); - -/// This struct is an ANSI escape code implementation for color related actions. -pub struct AnsiColor; - -impl AnsiColor { - pub fn new() -> AnsiColor { - AnsiColor - } -} - -impl ITerminalColor for AnsiColor { - fn set_fg(&self, fg_color: Color) -> Result<()> { - write_cout!(get_set_fg_ansi(fg_color))?; - Ok(()) - } - - fn set_bg(&self, bg_color: Color) -> Result<()> { - write_cout!(get_set_bg_ansi(bg_color))?; - Ok(()) - } - - fn reset(&self) -> Result<()> { - write_cout!(RESET_ANSI)?; - Ok(()) - } -} - -fn color_value(colored: Colored) -> String { - let mut ansi_value = String::new(); - - let color; - - match colored { - Colored::Fg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("39"); - return ansi_value; - } else { - ansi_value.push_str("38;"); - color = new_color; - } - } - Colored::Bg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("49"); - return ansi_value; - } else { - ansi_value.push_str("48;"); - color = new_color; - } - } - } - - let rgb_val: String; - - let color_val = match color { - Color::Black => "5;0", - Color::DarkGrey => "5;8", - Color::Red => "5;9", - Color::DarkRed => "5;1", - Color::Green => "5;10", - Color::DarkGreen => "5;2", - Color::Yellow => "5;11", - Color::DarkYellow => "5;3", - Color::Blue => "5;12", - Color::DarkBlue => "5;4", - Color::Magenta => "5;13", - Color::DarkMagenta => "5;5", - Color::Cyan => "5;14", - Color::DarkCyan => "5;6", - Color::White => "5;15", - Color::Grey => "5;7", - Color::Rgb { r, g, b } => { - rgb_val = format!("2;{};{};{}", r, g, b); - rgb_val.as_str() - } - Color::AnsiValue(val) => { - rgb_val = format!("5;{}", val); - rgb_val.as_str() - } - _ => "", - }; - - ansi_value.push_str(color_val); - ansi_value -} diff --git a/src/color.rs b/src/color.rs index 7bccfa0..077d8d0 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,166 +1,26 @@ //! A module that contains all the actions related to the styling of the terminal. //! Like applying attributes to text and changing the foreground and background. -use std::clone::Clone; -use std::env; -use std::fmt::Display; - +pub(crate) mod ansi; #[cfg(windows)] -use crossterm_utils::supports_ansi; -use crossterm_utils::{impl_display, Command, Result}; +pub(crate) mod winapi; -use super::ansi_color::{self, AnsiColor}; -use super::enums::{Attribute, Color}; -use super::styledobject::StyledObject; -#[cfg(windows)] -use super::winapi_color::WinApiColor; -use super::ITerminalColor; +use super::ColorType; +use crossterm_utils::Result; -/// Allows you to style the terminal. -/// -/// # Features: +/// This trait defines the actions that can be performed with terminal colors. +/// This trait can be implemented so that a concrete implementation of the ITerminalColor can fulfill +/// the wishes to work on a specific platform. /// -/// - Foreground color (16 base colors) -/// - Background color (16 base colors) -/// - 256 color support (Windows 10 and UNIX only) -/// - RGB support (Windows 10 and UNIX only) -/// - Text Attributes like: bold, italic, underscore and crossed word ect (Windows 10 and UNIX only) +/// ## For example: /// -/// Check `/examples/` in the library for more specific examples. -pub struct TerminalColor { - #[cfg(windows)] - color: Box<(dyn ITerminalColor + Sync + Send)>, - #[cfg(unix)] - color: AnsiColor, -} - -impl TerminalColor { - /// Create new instance whereon color related actions can be performed. - pub fn new() -> TerminalColor { - #[cfg(windows)] - let color = if supports_ansi() { - Box::from(AnsiColor::new()) as Box<(dyn ITerminalColor + Sync + Send)> - } else { - WinApiColor::new() as Box<(dyn ITerminalColor + Sync + Send)> - }; - - #[cfg(unix)] - let color = AnsiColor::new(); - - TerminalColor { color } - } - +/// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), +/// so that color-related actions can be performed on both UNIX and Windows systems. +pub trait Color { /// Set the foreground color to the given color. - pub fn set_fg(&self, color: Color) -> Result<()> { - self.color.set_fg(color) - } - + fn set_fg(&self, fg_color: ColorType) -> Result<()>; /// Set the background color to the given color. - pub fn set_bg(&self, color: Color) -> Result<()> { - self.color.set_bg(color) - } - - /// Reset the terminal colors and attributes to default. - pub fn reset(&self) -> Result<()> { - self.color.reset() - } - - /// Get available color count. - /// - /// # Remarks - /// - /// This does not always provide a good result. - pub fn available_color_count(&self) -> u16 { - env::var("TERM") - .map(|x| if x.contains("256color") { 256 } else { 8 }) - .unwrap_or(8) - } -} - -/// Get a `TerminalColor` implementation whereon color related actions can be performed. -pub fn color() -> TerminalColor { - TerminalColor::new() -} - -/// When executed, this command will set the foreground color of the terminal to the given color. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetFg(pub Color); - -impl Command for SetFg { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_fg_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiColor::new().set_fg(self.0) - } + fn set_bg(&self, fg_color: ColorType) -> Result<()>; + /// Reset the terminal color to default. + fn reset(&self) -> Result<()>; } - -/// When executed, this command will set the background color of the terminal to the given color. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetBg(pub Color); - -impl Command for SetBg { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_bg_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiColor::new().set_fg(self.0) - } -} - -/// When executed, this command will set the given attribute to the terminal. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetAttr(pub Attribute); - -impl Command for SetAttr { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_attr_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -/// When executed, this command will print the styled font to the terminal. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct PrintStyledFont(pub StyledObject); - -impl Command for PrintStyledFont -where - D: Display + Clone, -{ - type AnsiType = StyledObject; - - fn ansi_code(&self) -> Self::AnsiType { - self.0.clone() - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -impl_display!(for SetFg); -impl_display!(for SetBg); -impl_display!(for SetAttr); -impl_display!(for PrintStyledFont); -impl_display!(for PrintStyledFont<&'static str>); diff --git a/src/color/ansi.rs b/src/color/ansi.rs new file mode 100644 index 0000000..1d6cef5 --- /dev/null +++ b/src/color/ansi.rs @@ -0,0 +1,148 @@ +//! This is a ANSI specific implementation for styling related action. +//! This module is used for Windows 10 terminals and Unix terminals by default. + +use crossterm_utils::{csi, write_cout, Result}; + +use crate::{Attribute, Color, ColorType, Colored}; + +pub(crate) fn set_fg_sequence(fg_color: ColorType) -> String { + format!(csi!("{}m"), color_value(Colored::Fg(fg_color))) +} + +pub(crate) fn set_bg_csi_sequence(bg_color: ColorType) -> String { + format!(csi!("{}m"), color_value(Colored::Bg(bg_color))) +} + +pub(crate) fn set_attr_csi_sequence(attribute: Attribute) -> String { + format!(csi!("{}m"), attribute as i16) +} + +pub(crate) static RESET_CSI_SEQUENCE: &'static str = csi!("0m"); + +/// This struct is an ANSI escape code implementation for color related actions. +pub(crate) struct AnsiColor; + +impl AnsiColor { + pub fn new() -> AnsiColor { + AnsiColor + } +} + +impl Color for AnsiColor { + fn set_fg(&self, fg_color: ColorType) -> Result<()> { + write_cout!(set_fg_sequence(fg_color))?; + Ok(()) + } + + fn set_bg(&self, bg_color: ColorType) -> Result<()> { + write_cout!(set_bg_csi_sequence(bg_color))?; + Ok(()) + } + + fn reset(&self) -> Result<()> { + write_cout!(RESET_CSI_SEQUENCE)?; + Ok(()) + } +} + +fn color_value(colored: Colored) -> String { + let mut ansi_value = String::new(); + + let color; + + match colored { + Colored::Fg(new_color) => { + if new_color == ColorType::Reset { + ansi_value.push_str("39"); + return ansi_value; + } else { + ansi_value.push_str("38;"); + color = new_color; + } + } + Colored::Bg(new_color) => { + if new_color == ColorType::Reset { + ansi_value.push_str("49"); + return ansi_value; + } else { + ansi_value.push_str("48;"); + color = new_color; + } + } + } + + let rgb_val: String; + + let color_val = match color { + ColorType::Black => "5;0", + ColorType::DarkGrey => "5;8", + ColorType::Red => "5;9", + ColorType::DarkRed => "5;1", + ColorType::Green => "5;10", + ColorType::DarkGreen => "5;2", + ColorType::Yellow => "5;11", + ColorType::DarkYellow => "5;3", + ColorType::Blue => "5;12", + ColorType::DarkBlue => "5;4", + ColorType::Magenta => "5;13", + ColorType::DarkMagenta => "5;5", + ColorType::Cyan => "5;14", + ColorType::DarkCyan => "5;6", + ColorType::White => "5;15", + ColorType::Grey => "5;7", + ColorType::Rgb { r, g, b } => { + rgb_val = format!("2;{};{};{}", r, g, b); + rgb_val.as_str() + } + ColorType::AnsiValue(val) => { + rgb_val = format!("5;{}", val); + rgb_val.as_str() + } + _ => "", + }; + + ansi_value.push_str(color_val); + ansi_value +} + +#[cfg(test)] +mod tests { + use crate::color::ansi::color_value; + use crate::{ColorType, Colored}; + + #[test] + fn test_parse_fg_color() { + let colored = Colored::Fg(ColorType::Red); + assert_eq!(color_value(colored), "38;5;9"); + } + + #[test] + fn test_parse_bg_color() { + let colored = Colored::Bg(ColorType::Red); + assert_eq!(color_value(colored), "48;5;9"); + } + + #[test] + fn test_parse_reset_fg_color() { + let colored = Colored::Fg(ColorType::Reset); + assert_eq!(color_value(colored), "39"); + } + + #[test] + fn test_parse_reset_bg_color() { + let colored = Colored::Bg(ColorType::Reset); + assert_eq!(color_value(colored), "49"); + } + + #[test] + fn test_parse_fg_rgb_color() { + let colored = Colored::Bg(ColorType::Rgb { r: 1, g: 2, b: 3 }); + assert_eq!(color_value(colored), "48;2;1;2;3"); + } + + #[test] + fn test_parse_fg_ansi_color() { + let colored = Colored::Fg(ColorType::AnsiValue(255)); + assert_eq!(color_value(colored), "38;5;255"); + } +} diff --git a/src/winapi_color.rs b/src/color/winapi.rs similarity index 62% rename from src/winapi_color.rs rename to src/color/winapi.rs index 9da9419..4b3b123 100644 --- a/src/winapi_color.rs +++ b/src/color/winapi.rs @@ -8,7 +8,7 @@ use winapi::um::wincon; use crossterm_utils::Result; use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; -use crate::{Color, Colored, ITerminalColor}; +use crate::{Color, ColorType, Colored}; const FG_GREEN: u16 = wincon::FOREGROUND_GREEN; const FG_RED: u16 = wincon::FOREGROUND_RED; @@ -29,8 +29,8 @@ impl WinApiColor { } } -impl ITerminalColor for WinApiColor { - fn set_fg(&self, fg_color: Color) -> Result<()> { +impl Color for WinApiColor { + fn set_fg(&self, fg_color: ColorType) -> Result<()> { // init the original color in case it is not set. init_console_color()?; @@ -44,7 +44,7 @@ impl ITerminalColor for WinApiColor { let mut color: u16; let attrs = csbi.attributes(); let bg_color = attrs & 0x0070; - color = color_value.parse::()? | bg_color; + color = color_value | bg_color; // background intensity is a separate value in attrs, // wee need to check if this was applied to the current bg color. @@ -57,7 +57,7 @@ impl ITerminalColor for WinApiColor { Ok(()) } - fn set_bg(&self, bg_color: Color) -> Result<()> { + fn set_bg(&self, bg_color: ColorType) -> Result<()> { // init the original color in case it is not set. init_console_color()?; @@ -71,7 +71,7 @@ impl ITerminalColor for WinApiColor { let mut color: u16; let attrs = csbi.attributes(); let fg_color = attrs & 0x0007; - color = fg_color | color_value.parse::()?; + color = fg_color | color_value; // Foreground intensity is a separate value in attrs, // So we need to check if this was applied to the current fg color. @@ -95,30 +95,30 @@ impl ITerminalColor for WinApiColor { } /// This will get the winapi color value from the Color and ColorType struct -fn color_value(color: Colored) -> String { +fn color_value(color: Colored) -> u16 { let winapi_color: u16; match color { Colored::Fg(color) => { winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => FG_INTENSITY, - Color::Red => FG_INTENSITY | FG_RED, - Color::DarkRed => FG_RED, - Color::Green => FG_INTENSITY | FG_GREEN, - Color::DarkGreen => FG_GREEN, - Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, - Color::DarkYellow => FG_GREEN | FG_RED, - Color::Blue => FG_INTENSITY | FG_BLUE, - Color::DarkBlue => FG_BLUE, - Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, - Color::DarkMagenta => FG_RED | FG_BLUE, - Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, - Color::DarkCyan => FG_GREEN | FG_BLUE, - Color::White => FG_RED | FG_GREEN | FG_BLUE, - Color::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, - - Color::Reset => { + ColorType::Black => 0, + ColorType::DarkGrey => FG_INTENSITY, + ColorType::Red => FG_INTENSITY | FG_RED, + ColorType::DarkRed => FG_RED, + ColorType::Green => FG_INTENSITY | FG_GREEN, + ColorType::DarkGreen => FG_GREEN, + ColorType::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, + ColorType::DarkYellow => FG_GREEN | FG_RED, + ColorType::Blue => FG_INTENSITY | FG_BLUE, + ColorType::DarkBlue => FG_BLUE, + ColorType::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, + ColorType::DarkMagenta => FG_RED | FG_BLUE, + ColorType::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, + ColorType::DarkCyan => FG_GREEN | FG_BLUE, + ColorType::White => FG_RED | FG_GREEN | FG_BLUE, + ColorType::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, + + ColorType::Reset => { // init the original color in case it is not set. let mut original_color = original_console_color(); @@ -130,30 +130,30 @@ fn color_value(color: Colored) -> String { } /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, + ColorType::Rgb { r: _, g: _, b: _ } => 0, + ColorType::AnsiValue(_val) => 0, }; } Colored::Bg(color) => { winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => BG_INTENSITY, - Color::Red => BG_INTENSITY | BG_RED, - Color::DarkRed => BG_RED, - Color::Green => BG_INTENSITY | BG_GREEN, - Color::DarkGreen => BG_GREEN, - Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, - Color::DarkYellow => BG_GREEN | BG_RED, - Color::Blue => BG_INTENSITY | BG_BLUE, - Color::DarkBlue => BG_BLUE, - Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, - Color::DarkMagenta => BG_RED | BG_BLUE, - Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, - Color::DarkCyan => BG_GREEN | BG_BLUE, - Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, - Color::Grey => BG_RED | BG_GREEN | BG_BLUE, - - Color::Reset => { + ColorType::Black => 0, + ColorType::DarkGrey => BG_INTENSITY, + ColorType::Red => BG_INTENSITY | BG_RED, + ColorType::DarkRed => BG_RED, + ColorType::Green => BG_INTENSITY | BG_GREEN, + ColorType::DarkGreen => BG_GREEN, + ColorType::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, + ColorType::DarkYellow => BG_GREEN | BG_RED, + ColorType::Blue => BG_INTENSITY | BG_BLUE, + ColorType::DarkBlue => BG_BLUE, + ColorType::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, + ColorType::DarkMagenta => BG_RED | BG_BLUE, + ColorType::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, + ColorType::DarkCyan => BG_GREEN | BG_BLUE, + ColorType::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, + ColorType::Grey => BG_RED | BG_GREEN | BG_BLUE, + + ColorType::Reset => { // init the original color in case it is not set. let mut original_color = original_console_color(); @@ -163,13 +163,13 @@ fn color_value(color: Colored) -> String { original_color } /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, + ColorType::Rgb { r: _, g: _, b: _ } => 0, + ColorType::AnsiValue(_val) => 0, }; } }; - winapi_color.to_string() + winapi_color } fn init_console_color() -> Result<()> { @@ -189,3 +189,21 @@ fn original_console_color() -> u16 { static GET_ORIGINAL_CONSOLE_COLOR: Once = Once::new(); static mut ORIGINAL_CONSOLE_COLOR: u16 = 0; + +#[cfg(test)] +mod tests { + use crate::color::winapi::{color_value, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED}; + use crate::{ColorType, Colored}; + + #[test] + fn test_parse_fg_color() { + let colored = Colored::Fg(ColorType::Red); + assert_eq!(color_value(colored), FG_INTENSITY | FG_RED); + } + + #[test] + fn test_parse_bg_color() { + let colored = Colored::Bg(ColorType::Red); + assert_eq!(color_value(colored), BG_INTENSITY | BG_RED); + } +} diff --git a/src/enums.rs b/src/enums.rs index e790c64..e46e1dd 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1,5 +1,5 @@ -pub use self::{attribute::Attribute, color::Color, colored::Colored}; +pub use self::{attribute::Attribute, color_type::ColorType, colored::Colored}; mod attribute; -mod color; +mod color_type; mod colored; diff --git a/src/enums/attribute.rs b/src/enums/attribute.rs index 408020d..e96c6f2 100644 --- a/src/enums/attribute.rs +++ b/src/enums/attribute.rs @@ -3,7 +3,7 @@ use std::fmt::Display; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crossterm_utils::csi; +use crate::SetAttr; /// Enum with the different attributes to style your test. /// @@ -17,7 +17,9 @@ use crossterm_utils::csi; /// # Example /// You can use an attribute in a write statement to apply the attribute to the terminal output. /// -/// ```ignore +/// ```no_run +/// use crossterm_style::Attribute; +/// /// println!( /// "{} Underlined {} No Underline", /// Attribute::Underlined, @@ -26,12 +28,12 @@ use crossterm_utils::csi; /// ``` /// /// You can also call attribute functions on a `&'static str`: -/// ```ignore -/// use Colorizer; +/// ```no_run +/// use crossterm_style::Styler; /// -/// println!("{}", style("Bold text").bold()); -/// println!("{}", style("Underlined text").underlined()); -/// println!("{}", style("Negative text").negative()); +/// println!("{}", "Bold text".bold()); +/// println!("{}", "Underlined text".underlined()); +/// println!("{}", "Negative text".negative()); /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] @@ -142,7 +144,7 @@ pub enum Attribute { impl Display for Attribute { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { - write!(f, "{}", format!(csi!("{}m"), *self as i16))?; + write!(f, "{}", SetAttr(*self))?; Ok(()) } } diff --git a/src/enums/color.rs b/src/enums/color.rs deleted file mode 100644 index d17303d..0000000 --- a/src/enums/color.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::convert::AsRef; -use std::str::FromStr; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Enum with the different colors to color your test and terminal. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Color { - // This resets the color. - Reset, - - Black, - DarkGrey, - - Red, - DarkRed, - - Green, - DarkGreen, - - Yellow, - DarkYellow, - - Blue, - DarkBlue, - - Magenta, - DarkMagenta, - - Cyan, - DarkCyan, - - White, - Grey, - /// Color representing RGB-colors; - /// r = red - /// g = green - /// b = blue - Rgb { - r: u8, - g: u8, - b: u8, - }, - AnsiValue(u8), -} - -impl FromStr for Color { - type Err = (); - - /// Creates a `Color` from the string representation. - /// - /// # Remarks - /// - /// * `Color::White` is returned in case of an unknown color. - /// * This function does not return `Err` and you can safely unwrap. - fn from_str(src: &str) -> ::std::result::Result { - let src = src.to_lowercase(); - - match src.as_ref() { - "black" => Ok(Color::Black), - "dark_grey" => Ok(Color::DarkGrey), - "red" => Ok(Color::Red), - "dark_red" => Ok(Color::DarkRed), - "green" => Ok(Color::Green), - "dark_green" => Ok(Color::DarkGreen), - "yellow" => Ok(Color::Yellow), - "dark_yellow" => Ok(Color::DarkYellow), - "blue" => Ok(Color::Blue), - "dark_blue" => Ok(Color::DarkBlue), - "magenta" => Ok(Color::Magenta), - "dark_magenta" => Ok(Color::DarkMagenta), - "cyan" => Ok(Color::Cyan), - "dark_cyan" => Ok(Color::DarkCyan), - "white" => Ok(Color::White), - "grey" => Ok(Color::Grey), - _ => Ok(Color::White), - } - } -} - -#[cfg(test)] -mod tests { - use super::Color; - - #[test] - fn test_known_color_conversion() { - assert_eq!("black".parse(), Ok(Color::Black)); - assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); - assert_eq!("red".parse(), Ok(Color::Red)); - assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); - assert_eq!("green".parse(), Ok(Color::Green)); - assert_eq!("dark_green".parse(), Ok(Color::DarkGreen)); - assert_eq!("yellow".parse(), Ok(Color::Yellow)); - assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow)); - assert_eq!("blue".parse(), Ok(Color::Blue)); - assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue)); - assert_eq!("magenta".parse(), Ok(Color::Magenta)); - assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta)); - assert_eq!("cyan".parse(), Ok(Color::Cyan)); - assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); - assert_eq!("white".parse(), Ok(Color::White)); - assert_eq!("grey".parse(), Ok(Color::Grey)); - } - - #[test] - fn test_unknown_color_conversion_yields_white() { - assert_eq!("foo".parse(), Ok(Color::White)); - } -} diff --git a/src/enums/color_type.rs b/src/enums/color_type.rs new file mode 100644 index 0000000..8fabe17 --- /dev/null +++ b/src/enums/color_type.rs @@ -0,0 +1,111 @@ +use std::convert::AsRef; +use std::str::FromStr; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Enum with the different colors to color your test and terminal. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum ColorType { + // This resets the color. + Reset, + + Black, + DarkGrey, + + Red, + DarkRed, + + Green, + DarkGreen, + + Yellow, + DarkYellow, + + Blue, + DarkBlue, + + Magenta, + DarkMagenta, + + Cyan, + DarkCyan, + + White, + Grey, + /// Color representing RGB-colors; + /// r = red + /// g = green + /// b = blue + Rgb { + r: u8, + g: u8, + b: u8, + }, + AnsiValue(u8), +} + +impl FromStr for ColorType { + type Err = (); + + /// Creates a `Color` from the string representation. + /// + /// # Remarks + /// + /// * `ColorType::White` is returned in case of an unknown color. + /// * This function does not return `Err` and you can safely unwrap. + fn from_str(src: &str) -> ::std::result::Result { + let src = src.to_lowercase(); + + match src.as_ref() { + "black" => Ok(ColorType::Black), + "dark_grey" => Ok(ColorType::DarkGrey), + "red" => Ok(ColorType::Red), + "dark_red" => Ok(ColorType::DarkRed), + "green" => Ok(ColorType::Green), + "dark_green" => Ok(ColorType::DarkGreen), + "yellow" => Ok(ColorType::Yellow), + "dark_yellow" => Ok(ColorType::DarkYellow), + "blue" => Ok(ColorType::Blue), + "dark_blue" => Ok(ColorType::DarkBlue), + "magenta" => Ok(ColorType::Magenta), + "dark_magenta" => Ok(ColorType::DarkMagenta), + "cyan" => Ok(ColorType::Cyan), + "dark_cyan" => Ok(ColorType::DarkCyan), + "white" => Ok(ColorType::White), + "grey" => Ok(ColorType::Grey), + _ => Ok(ColorType::White), + } + } +} + +#[cfg(test)] +mod tests { + use super::ColorType; + + #[test] + fn test_known_color_conversion() { + assert_eq!("black".parse(), Ok(ColorType::Black)); + assert_eq!("dark_grey".parse(), Ok(ColorType::DarkGrey)); + assert_eq!("red".parse(), Ok(ColorType::Red)); + assert_eq!("dark_red".parse(), Ok(ColorType::DarkRed)); + assert_eq!("green".parse(), Ok(ColorType::Green)); + assert_eq!("dark_green".parse(), Ok(ColorType::DarkGreen)); + assert_eq!("yellow".parse(), Ok(ColorType::Yellow)); + assert_eq!("dark_yellow".parse(), Ok(ColorType::DarkYellow)); + assert_eq!("blue".parse(), Ok(ColorType::Blue)); + assert_eq!("dark_blue".parse(), Ok(ColorType::DarkBlue)); + assert_eq!("magenta".parse(), Ok(ColorType::Magenta)); + assert_eq!("dark_magenta".parse(), Ok(ColorType::DarkMagenta)); + assert_eq!("cyan".parse(), Ok(ColorType::Cyan)); + assert_eq!("dark_cyan".parse(), Ok(ColorType::DarkCyan)); + assert_eq!("white".parse(), Ok(ColorType::White)); + assert_eq!("grey".parse(), Ok(ColorType::Grey)); + } + + #[test] + fn test_unknown_color_conversion_yields_white() { + assert_eq!("foo".parse(), Ok(ColorType::White)); + } +} diff --git a/src/enums/colored.rs b/src/enums/colored.rs index 1073225..e795cf2 100644 --- a/src/enums/colored.rs +++ b/src/enums/colored.rs @@ -3,33 +3,27 @@ use std::fmt::Display; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::color::color; -use crate::enums::Color; +use crate::color; +use crate::enums::ColorType; -/// Could be used to color the foreground or background color. -/// -/// `Colored::Fg` represents the foreground color. -/// `Color::Bg` represents the background color. +/// Can be used to easily change the front and back ground color /// /// # Example /// -/// You can use `Colored` in a write statement to apply the attribute to the terminal output. -/// -/// ```ignore -/// println!("{} Red foreground color", Colored::Fg(Color::Red)); -/// println!("{} Blue background color", Colored::Bg(Color::Blue)); -/// ``` +/// `Colored` implements `Display` therefore you can use it in any `write` operation. /// -/// You can also call coloring functions on a `&'static str`: -/// ```ignore -/// let styled_text = "Red forground color on blue background.".red().on_blue(); -/// println!("{}", styled_text); +/// ```no_run +/// use crossterm_style::{Colored, ColorType}; +/// println!("{} Red foreground color", Colored::Fg(ColorType::Red)); +/// println!("{} Blue background color", Colored::Bg(ColorType::Blue)); /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum Colored { - Fg(Color), - Bg(Color), + /// Use this if you want to change the foreground color + Fg(ColorType), + /// Use this if you want to change the background color + Bg(ColorType), } impl Display for Colored { diff --git a/src/lib.rs b/src/lib.rs index cf8fbb5..1ee79e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,201 @@ -//! A module that contains all the actions related to the styling of the terminal. -//! Like applying attributes to text and changing the foreground and background. +//! # Styling Module +//! +//! Crossterm provides options for you to style your text and terminal. Take for example coloring output and applying attributes. +//! +//! **Color support** +//! Windows systems with versions less than 10 will only have support for 16 colors and don't have any support for attributes. Most UNIX-terminal is supporting lots of colors and attributes. +//! +//! ## Colors +//! There are 16 base colors which available for almost all terminals even windows 7 and 8. +//! +//! | Light Variant | Dark Variant | +//! | :-------------| :------------- | +//! | Grey | Black | +//! | Red | DarkRed | +//! | Green | DarkGreen | +//! | Yellow | DarkYellow | +//! | Blue | DarkBlue | +//! | Magenta | DarkMagenta| +//! | Cyan | DarkCyan | +//! | White | DarkWhite | +//! +//! In addition to 16 colors, most UNIX terminals and Windows 10 consoles are also supporting more colors. +//! Those colors could be: [True color (24-bit)](https://en.wikipedia.org/wiki/Color_depth#True_color_(24-bit)) coloring scheme, which allows you to use [RGB](https://nl.wikipedia.org/wiki/RGB-kleursysteem), and [256 (Xterm, 8-bit)](https://jonasjacek.github.io/colors/) colors. +//! Checkout the [examples](https://github.com/crossterm-rs/crossterm/blob/master/examples/style.rs) on how to use this feature. +//! +//! ## Attributes +//! Only UNIX and Windows 10 terminals are supporting attributes on top of the text. Crossterm allows you to add attributes to the text. +//! Not all attributes are widely supported for all terminals, keep that in mind when working with this. +//! +//! Crossterm implements almost all attributes shown in this [Wikipedia-list](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters). +//! +//! | Attribute | Support | Note | +//! | :-------------: | :-------------: | :-------------: | +//! | Reset | Windows, UNIX | This will reset all current set attributes. | +//! | Bold | Windows, UNIX | This will increase the text sensitivity also known as bold. | +//! | Dim | Windows, UNIX | This will decrease the text sensitivity also known as bold. | +//! | Italic | Not widely supported, sometimes treated as inverse. | This will make the text italic. | +//! | Underlined | Windows, UNIX | A line under a word, especially in order to show its importance. | +//! | SlowBlink | Not widely supported, sometimes treated as inverse. | less than 150 per minute | +//! | RapidBlink | Not widely supported | MS-DOS ANSI.SYS; 150+ per minute; | +//! | Reverse | Windows, UNIX | foreground and background colors | +//! | Hidden | Windows, UNIX | | Also known as 'Conceal' +//! | Fraktur | UNIX | characters legible, but marked for deletion. | +//! | DefaultForegroundColor | Unknown | Implementation defined (according to standard) | +//! | DefaultBackgroundColor | Unknown | Implementation defined (according to standard) | +//! | Framed | Not widely supported | Framed text. +//! | Encircled | Unknown | This will turn on the encircled attribute. | +//! | OverLined | Unknown | This will draw a line at the top of the text. | +//! +//! (There are a few attributes who disable one of the above attributes, I did not write those down to keep the list short). +//! +//! Now we have covered the basics of styling lets go over to some examples. +//! +//! +//! # Example +//! +//! _setup the basics_ +//! ```no_run +//! use crossterm_style::{Colored, ColorType, Attribute, Styler, Colorize}; +//! +//! fn main() { +//! /* your code here */ +//! } +//! ``` +//! +//! There are a couple of ways to style the terminal output with crossterm. The most important part of the styling module is `StyledObject`. +//! +//! A `StyledObject` is just a wrapper crossterm uses to store the text and style together. +//! A `StyledObject` implements `Display` and thus you could use it inside `print!`, `println!` etc. +//! +//! Without further ado let's get straight into it. +//! +//! ## Coloring +//! +//! There are a few ways to do the coloring, the first one is by using the `Colored` enum. +//! +//! ### Using Enum +//! ```no_run +//! use crossterm_style::{Colored, ColorType}; +//! println!("{} Red foreground color", Colored::Fg(ColorType::Red)); +//! println!("{} Blue background color", Colored::Bg(ColorType::Blue)); +//! ``` +//! `Colored::Bg` will set the background color, and `Colored::Fg` will set the foreground color to the provided color. +//! The provided color is of type `Color` and has a bunch of enum values you could choose out. +//! +//! Because `Colored` implements `Display` you are able to use it inside any write statement. +//! +//! ### Using Methods +//! You can do the same as the above in a slightly different way. Instead of enabling it for all text you could also color the only piece of text. +//! (Make sure to include the `crossterm::Coloring` trait). +//! +//! ```no_run +//! use crossterm_style::Colorize; +//! let styled_text = "Red forground color on blue background.".red().on_blue(); +//! println!("{}", styled_text); +//! ``` +//! +//! As you see in the above example you could call coloring methods on a string. How is this possible you might ask..? +//! Well, the trait `Coloring`, who you need to include, is implemented for `&'static str`. +//! When calling a method on this string crossterm transforms it into a `StyledObject` who you could use in your write statements. +//! +//! +//! ### RGB +//! Most UNIX terminals and all Windows 10 consoles are supporting [True color(24-bit)](https://en.wikipedia.org/wiki/Color_depth#True_color_(24-bit)) coloring scheme. +//! You can set the color of the terminal by using `ColorType::RGB(r,g,b)`. +//! +//! ```no_run +//! // custom rgb value (Windows 10 and UNIX systems) +//! use crossterm_style::{Colored, ColorType}; +//! println!("{}{} 'Light green' text on 'Black' background", Colored::Fg(ColorType::Rgb { r: 0, g: 255, b: 128 }), Colored::Bg(ColorType::Rgb {r: 0, g: 0, b: 0})); +//! ``` +//! This will print some light green text on black background. +//! +//! ### Custom ANSI color value +//! When working on UNIX or Windows 10 you could also specify a custom ANSI value ranging up from 0 to 256. +//! See [256 (Xterm, 8-bit) colors](https://jonasjacek.github.io/colors/) for more information. +//! +//! ``` +//! // custom ansi color value (Windows 10 and UNIX systems) +//! use crossterm_style::{Colored, ColorType}; +//! println!("{} some colored text", Colored::Fg(ColorType::AnsiValue(10))); +//! ``` +//! +//! ## Attributes +//! When working with UNIX or Windows 10 terminals you could also use attributes to style your text. For example, you could cross your text with a line and make it bold. +//! See [this](styling.md#Attributes) for more information. +//! +//! ### Using Enum +//! You could use the `Attribute` enum for styling text with attributes. +//! `Attribute` implements `Display`, thus crossterm will enable the attribute style when using it in any writing operation. +//! +//! ```rust +//! use crossterm_style::Attribute; +//! println!( +//! "{} Underlined {} No Underline", +//! Attribute::Underlined, +//! Attribute::NoUnderline +//! ); +//! ``` +//! +//! ### Using Method +//! +//! You can do the same as the above in a slightly different way. Instead of enabling it for all text you could also style only one piece of text. +//! (Make sure to include the `crossterm::Styler` trait). +//! +//! ```no_run +//! use crossterm_style::Styler; +//! println!("{}", "Bold text".bold()); +//! println!("{}", "Underlined text".underlined()); +//! println!("{}", "Negative text".negative()); +//! ``` +//! +//! ### Using Command API +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! +//! use crossterm_utils::{execute, Result, Output}; +//! use crossterm_style::{SetBg, SetFg, SetAttr, ColorType, Attribute}; +//! +//! fn main() -> Result<()> { +//! execute!( +//! stdout(), +//! SetFg(ColorType::Blue), +//! SetBg(ColorType::Red), +//! Output("Blue text on red background".to_string()), +//! SetAttr(Attribute::Reset) +//! ) +//! } +//! ``` +//! +//! As you see in the above example you could call attributes methods on a string. How is this possible you might ask..? +//! Well, the trait `Styling`, who you need to include, is implemented for `&'static str`. +//! When calling a method on any string crossterm transforms will transform it into a `StyledObject` who you could use in your write statements. +//! +//! --------------------------------------------------------------------------------------------------------------------------------------------- +//! More examples could be found at this [link](https://github.com/crossterm-rs/crossterm/blob/master/examples/style.rs). + #![deny(unused_imports)] use std::fmt::Display; -pub use crossterm_utils::{execute, queue, Command, ExecutableCommand, QueueableCommand, Result}; +pub use crossterm_utils::{ + execute, impl_display, queue, supports_ansi, Command, ExecutableCommand, QueueableCommand, + Result, +}; -pub use self::color::{color, PrintStyledFont, SetAttr, SetBg, SetFg, TerminalColor}; -pub use self::enums::{Attribute, Color, Colored}; +pub use self::enums::{Attribute, ColorType, Colored}; pub use self::objectstyle::ObjectStyle; pub use self::styledobject::StyledObject; pub use self::traits::{Colorize, Styler}; +use color::ansi::{self, AnsiColor}; +#[cfg(windows)] +use color::winapi::WinApiColor; +use color::Color; +use std::env; + #[macro_use] mod macros; mod color; @@ -20,35 +204,14 @@ pub mod objectstyle; pub mod styledobject; mod traits; -mod ansi_color; -#[cfg(windows)] -mod winapi_color; - -/// This trait defines the actions that can be performed with terminal colors. -/// This trait can be implemented so that a concrete implementation of the ITerminalColor can fulfill -/// the wishes to work on a specific platform. -/// -/// ## For example: -/// -/// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), -/// so that color-related actions can be performed on both UNIX and Windows systems. -trait ITerminalColor { - /// Set the foreground color to the given color. - fn set_fg(&self, fg_color: Color) -> Result<()>; - /// Set the background color to the given color. - fn set_bg(&self, fg_color: Color) -> Result<()>; - /// Reset the terminal color to default. - fn reset(&self) -> Result<()>; -} - /// This could be used to style a type that implements `Display` with colors and attributes. /// /// # Example /// ```ignore /// // get a styled object which could be painted to the terminal. /// let styled_object = style("Some Blue colored text on black background") -/// .with(Color::Blue) -/// .on(Color::Black); +/// .with(ColorType::Blue) +/// .on(ColorType::Black); /// /// // print the styled text * times to the current screen. /// for i in 1..10 @@ -70,40 +233,40 @@ where impl Colorize<&'static str> for &'static str { // foreground colors - def_str_color!(fg_color: black => Color::Black); - def_str_color!(fg_color: dark_grey => Color::DarkGrey); - def_str_color!(fg_color: red => Color::Red); - def_str_color!(fg_color: dark_red => Color::DarkRed); - def_str_color!(fg_color: green => Color::Green); - def_str_color!(fg_color: dark_green => Color::DarkGreen); - def_str_color!(fg_color: yellow => Color::Yellow); - def_str_color!(fg_color: dark_yellow => Color::DarkYellow); - def_str_color!(fg_color: blue => Color::Blue); - def_str_color!(fg_color: dark_blue => Color::DarkBlue); - def_str_color!(fg_color: magenta => Color::Magenta); - def_str_color!(fg_color: dark_magenta => Color::DarkMagenta); - def_str_color!(fg_color: cyan => Color::Cyan); - def_str_color!(fg_color: dark_cyan => Color::DarkCyan); - def_str_color!(fg_color: white => Color::White); - def_str_color!(fg_color: grey => Color::Grey); + def_str_color!(fg_color: black => ColorType::Black); + def_str_color!(fg_color: dark_grey => ColorType::DarkGrey); + def_str_color!(fg_color: red => ColorType::Red); + def_str_color!(fg_color: dark_red => ColorType::DarkRed); + def_str_color!(fg_color: green => ColorType::Green); + def_str_color!(fg_color: dark_green => ColorType::DarkGreen); + def_str_color!(fg_color: yellow => ColorType::Yellow); + def_str_color!(fg_color: dark_yellow => ColorType::DarkYellow); + def_str_color!(fg_color: blue => ColorType::Blue); + def_str_color!(fg_color: dark_blue => ColorType::DarkBlue); + def_str_color!(fg_color: magenta => ColorType::Magenta); + def_str_color!(fg_color: dark_magenta => ColorType::DarkMagenta); + def_str_color!(fg_color: cyan => ColorType::Cyan); + def_str_color!(fg_color: dark_cyan => ColorType::DarkCyan); + def_str_color!(fg_color: white => ColorType::White); + def_str_color!(fg_color: grey => ColorType::Grey); // background colors - def_str_color!(bg_color: on_black => Color::Black); - def_str_color!(bg_color: on_dark_grey => Color::DarkGrey); - def_str_color!(bg_color: on_red => Color::Red); - def_str_color!(bg_color: on_dark_red => Color::DarkRed); - def_str_color!(bg_color: on_green => Color::Green); - def_str_color!(bg_color: on_dark_green => Color::DarkGreen); - def_str_color!(bg_color: on_yellow => Color::Yellow); - def_str_color!(bg_color: on_dark_yellow => Color::DarkYellow); - def_str_color!(bg_color: on_blue => Color::Blue); - def_str_color!(bg_color: on_dark_blue => Color::DarkBlue); - def_str_color!(bg_color: on_magenta => Color::Magenta); - def_str_color!(bg_color: on_dark_magenta => Color::DarkMagenta); - def_str_color!(bg_color: on_cyan => Color::Cyan); - def_str_color!(bg_color: on_dark_cyan => Color::DarkCyan); - def_str_color!(bg_color: on_white => Color::White); - def_str_color!(bg_color: on_grey => Color::Grey); + def_str_color!(bg_color: on_black => ColorType::Black); + def_str_color!(bg_color: on_dark_grey => ColorType::DarkGrey); + def_str_color!(bg_color: on_red => ColorType::Red); + def_str_color!(bg_color: on_dark_red => ColorType::DarkRed); + def_str_color!(bg_color: on_green => ColorType::Green); + def_str_color!(bg_color: on_dark_green => ColorType::DarkGreen); + def_str_color!(bg_color: on_yellow => ColorType::Yellow); + def_str_color!(bg_color: on_dark_yellow => ColorType::DarkYellow); + def_str_color!(bg_color: on_blue => ColorType::Blue); + def_str_color!(bg_color: on_dark_blue => ColorType::DarkBlue); + def_str_color!(bg_color: on_magenta => ColorType::Magenta); + def_str_color!(bg_color: on_dark_magenta => ColorType::DarkMagenta); + def_str_color!(bg_color: on_cyan => ColorType::Cyan); + def_str_color!(bg_color: on_dark_cyan => ColorType::DarkCyan); + def_str_color!(bg_color: on_white => ColorType::White); + def_str_color!(bg_color: on_grey => ColorType::Grey); } impl Styler<&'static str> for &'static str { @@ -119,3 +282,172 @@ impl Styler<&'static str> for &'static str { def_str_attr!(hidden => Attribute::Hidden); def_str_attr!(crossed_out => Attribute::CrossedOut); } + +/// Allows you to style the terminal. +/// +/// # Features: +/// +/// - Foreground color (16 base colors) +/// - Background color (16 base colors) +/// - 256 color support (Windows 10 and UNIX only) +/// - RGB support (Windows 10 and UNIX only) +/// - Text Attributes like: bold, italic, underscore and crossed word ect (Windows 10 and UNIX only) +/// +/// Check `/examples/` in the library for more specific examples. +/// +/// ## Examples +/// +/// Basic usage: +/// +/// ```no_run +/// // You can replace the following line with `use crossterm::TerminalCursor;` +/// // if you're using the `crossterm` crate with the `cursor` feature enabled. +/// use crossterm_style::{Result, TerminalColor, ColorType}; +/// +/// fn main() -> Result<()> { +/// let color = TerminalColor::new(); +/// // set foreground color +/// color.set_fg(ColorType::Blue)?; +/// // set background color +/// color.set_bg(ColorType::Red)?; +/// // reset to the default colors +/// color.reset() +/// } +/// ``` +pub struct TerminalColor { + #[cfg(windows)] + color: Box<(dyn Color + Sync + Send)>, + #[cfg(unix)] + color: AnsiColor, +} + +impl TerminalColor { + /// Create new instance whereon color related actions can be performed. + pub fn new() -> TerminalColor { + #[cfg(windows)] + let color = if supports_ansi() { + Box::from(AnsiColor::new()) as Box<(dyn Color + Sync + Send)> + } else { + WinApiColor::new() as Box<(dyn Color + Sync + Send)> + }; + + #[cfg(unix)] + let color = AnsiColor::new(); + + TerminalColor { color } + } + + /// Set the foreground color to the given color. + pub fn set_fg(&self, color: ColorType) -> Result<()> { + self.color.set_fg(color) + } + + /// Set the background color to the given color. + pub fn set_bg(&self, color: ColorType) -> Result<()> { + self.color.set_bg(color) + } + + /// Reset the terminal colors and attributes to default. + pub fn reset(&self) -> Result<()> { + self.color.reset() + } + + /// Get available color count. + /// + /// # Remarks + /// + /// This does not always provide a good result. + pub fn available_color_count(&self) -> u16 { + env::var("TERM") + .map(|x| if x.contains("256color") { 256 } else { 8 }) + .unwrap_or(8) + } +} + +/// Get a `TerminalColor` implementation whereon color related actions can be performed. +pub fn color() -> TerminalColor { + TerminalColor::new() +} + +/// When executed, this command will set the foreground color of the terminal to the given color. +/// +/// See `crossterm/examples/command.rs` for more information on how to execute commands. +pub struct SetFg(pub ColorType); + +impl Command for SetFg { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::set_fg_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + WinApiColor::new().set_fg(self.0) + } +} + +/// When executed, this command will set the background color of the terminal to the given color. +/// +/// See `crossterm/examples/command.rs` for more information on how to execute commands. +pub struct SetBg(pub ColorType); + +impl Command for SetBg { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::set_bg_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + WinApiColor::new().set_fg(self.0) + } +} + +/// When executed, this command will set the given attribute to the terminal. +/// +/// See `crossterm/examples/command.rs` for more information on how to execute commands. +pub struct SetAttr(pub Attribute); + +impl Command for SetAttr { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::set_attr_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // attributes are not supported by WinAPI. + Ok(()) + } +} + +/// When executed, this command will print the styled font to the terminal. +/// +/// See `crossterm/examples/command.rs` for more information on how to execute commands. +pub struct PrintStyledFont(pub StyledObject); + +impl Command for PrintStyledFont +where + D: Display + Clone, +{ + type AnsiType = StyledObject; + + fn ansi_code(&self) -> Self::AnsiType { + self.0.clone() + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // attributes are not supported by WinAPI. + Ok(()) + } +} + +impl_display!(for SetFg); +impl_display!(for SetBg); +impl_display!(for SetAttr); +impl_display!(for PrintStyledFont); +impl_display!(for PrintStyledFont<&'static str>); diff --git a/src/objectstyle.rs b/src/objectstyle.rs index 4fb8608..767ff83 100644 --- a/src/objectstyle.rs +++ b/src/objectstyle.rs @@ -2,13 +2,13 @@ use std::fmt::Display; -use super::{Attribute, Color, StyledObject}; +use super::{Attribute, ColorType, StyledObject}; /// Struct that contains the style properties that can be applied to a displayable object. #[derive(Debug, Clone, Default)] pub struct ObjectStyle { - pub fg_color: Option, - pub bg_color: Option, + pub fg_color: Option, + pub bg_color: Option, pub attrs: Vec, } @@ -27,13 +27,13 @@ impl ObjectStyle { } /// Set the background color of `ObjectStyle` to the passed color. - pub fn bg(mut self, color: Color) -> ObjectStyle { + pub fn set_bg(mut self, color: ColorType) -> ObjectStyle { self.bg_color = Some(color); self } /// Set the foreground color of `ObjectStyle` to the passed color. - pub fn fg(mut self, color: Color) -> ObjectStyle { + pub fn set_fg(mut self, color: ColorType) -> ObjectStyle { self.fg_color = Some(color); self } @@ -43,3 +43,34 @@ impl ObjectStyle { self.attrs.push(attr); } } + +#[cfg(test)] +mod tests { + use crate::{Attribute, ColorType, ObjectStyle}; + + #[test] + fn test_set_fg_bg_add_attr() { + let mut object_style = ObjectStyle::new() + .set_fg(ColorType::Blue) + .set_bg(ColorType::Red); + object_style.add_attr(Attribute::Reset); + + assert_eq!(object_style.fg_color, Some(ColorType::Blue)); + assert_eq!(object_style.bg_color, Some(ColorType::Red)); + assert_eq!(object_style.attrs[0], Attribute::Reset); + } + + #[test] + fn test_apply_object_style_to_text() { + let mut object_style = ObjectStyle::new() + .set_fg(ColorType::Blue) + .set_bg(ColorType::Red); + object_style.add_attr(Attribute::Reset); + + let styled_object = object_style.apply_to("test"); + + assert_eq!(styled_object.object_style.fg_color, Some(ColorType::Blue)); + assert_eq!(styled_object.object_style.bg_color, Some(ColorType::Red)); + assert_eq!(styled_object.object_style.attrs[0], Attribute::Reset); + } +} diff --git a/src/styledobject.rs b/src/styledobject.rs index f0b3ec7..6f4522a 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -5,7 +5,7 @@ use std::result; use crossterm_utils::{csi, queue}; -use super::{color, Attribute, Color, Colorize, ObjectStyle, SetBg, SetFg, Styler}; +use super::{color, Attribute, ColorType, Colorize, ObjectStyle, SetBg, SetFg, Styler}; /// Contains both the style and the content which can be styled. #[derive(Clone)] @@ -15,34 +15,34 @@ pub struct StyledObject { } impl<'a, D: Display + 'a + Clone> StyledObject { - /// Set the foreground of the styled object to the passed `Color`. + /// Set the foreground color with the given color /// /// # Remarks /// /// This methods consumes 'self', and works like a builder. - /// By having this functionality you can do: `with().on().attr()` - pub fn with(mut self, foreground_color: Color) -> StyledObject { - self.object_style = self.object_style.fg(foreground_color); + /// You can do: `with().on().attr()` + pub fn with(mut self, foreground_color: ColorType) -> StyledObject { + self.object_style = self.object_style.set_fg(foreground_color); self } - /// Set the background of the styled object to the passed `Color`. + /// Set the background color with the given color /// /// # Remarks /// /// This methods consumes 'self', and works like a builder. - /// By having this functionality you can do: `with().on().attr()` - pub fn on(mut self, background_color: Color) -> StyledObject { - self.object_style = self.object_style.bg(background_color); + /// You can do: `with().on().attr()` + pub fn on(mut self, background_color: ColorType) -> StyledObject { + self.object_style = self.object_style.set_bg(background_color); self } - /// Set the attribute of an styled object to the passed `Attribute`. + /// Add an attribute to the styled object. /// /// # Remarks /// /// This methods consumes 'self', and works like a builder. - /// By having this functionality you can do: `with().on().attr()` + /// You can do: `with().on().attr()` pub fn attr(mut self, attr: Attribute) -> StyledObject { self.object_style.add_attr(attr); self @@ -80,40 +80,40 @@ impl Display for StyledObject { impl Colorize for StyledObject { // foreground colors - def_color!(fg_color: black => Color::Black); - def_color!(fg_color: dark_grey => Color::DarkGrey); - def_color!(fg_color: red => Color::Red); - def_color!(fg_color: dark_red => Color::DarkRed); - def_color!(fg_color: green => Color::Green); - def_color!(fg_color: dark_green => Color::DarkGreen); - def_color!(fg_color: yellow => Color::Yellow); - def_color!(fg_color: dark_yellow => Color::DarkYellow); - def_color!(fg_color: blue => Color::Blue); - def_color!(fg_color: dark_blue => Color::DarkBlue); - def_color!(fg_color: magenta => Color::Magenta); - def_color!(fg_color: dark_magenta => Color::DarkMagenta); - def_color!(fg_color: cyan => Color::Cyan); - def_color!(fg_color: dark_cyan => Color::DarkCyan); - def_color!(fg_color: white => Color::White); - def_color!(fg_color: grey => Color::Grey); + def_color!(fg_color: black => ColorType::Black); + def_color!(fg_color: dark_grey => ColorType::DarkGrey); + def_color!(fg_color: red => ColorType::Red); + def_color!(fg_color: dark_red => ColorType::DarkRed); + def_color!(fg_color: green => ColorType::Green); + def_color!(fg_color: dark_green => ColorType::DarkGreen); + def_color!(fg_color: yellow => ColorType::Yellow); + def_color!(fg_color: dark_yellow => ColorType::DarkYellow); + def_color!(fg_color: blue => ColorType::Blue); + def_color!(fg_color: dark_blue => ColorType::DarkBlue); + def_color!(fg_color: magenta => ColorType::Magenta); + def_color!(fg_color: dark_magenta => ColorType::DarkMagenta); + def_color!(fg_color: cyan => ColorType::Cyan); + def_color!(fg_color: dark_cyan => ColorType::DarkCyan); + def_color!(fg_color: white => ColorType::White); + def_color!(fg_color: grey => ColorType::Grey); // background colors - def_color!(bg_color: on_black => Color::Black); - def_color!(bg_color: on_dark_grey => Color::DarkGrey); - def_color!(bg_color: on_red => Color::Red); - def_color!(bg_color: on_dark_red => Color::DarkRed); - def_color!(bg_color: on_green => Color::Green); - def_color!(bg_color: on_dark_green => Color::DarkGreen); - def_color!(bg_color: on_yellow => Color::Yellow); - def_color!(bg_color: on_dark_yellow => Color::DarkYellow); - def_color!(bg_color: on_blue => Color::Blue); - def_color!(bg_color: on_dark_blue => Color::DarkBlue); - def_color!(bg_color: on_magenta => Color::Magenta); - def_color!(bg_color: on_dark_magenta => Color::DarkMagenta); - def_color!(bg_color: on_cyan => Color::Cyan); - def_color!(bg_color: on_dark_cyan => Color::DarkCyan); - def_color!(bg_color: on_white => Color::White); - def_color!(bg_color: on_grey => Color::Grey); + def_color!(bg_color: on_black => ColorType::Black); + def_color!(bg_color: on_dark_grey => ColorType::DarkGrey); + def_color!(bg_color: on_red => ColorType::Red); + def_color!(bg_color: on_dark_red => ColorType::DarkRed); + def_color!(bg_color: on_green => ColorType::Green); + def_color!(bg_color: on_dark_green => ColorType::DarkGreen); + def_color!(bg_color: on_yellow => ColorType::Yellow); + def_color!(bg_color: on_dark_yellow => ColorType::DarkYellow); + def_color!(bg_color: on_blue => ColorType::Blue); + def_color!(bg_color: on_dark_blue => ColorType::DarkBlue); + def_color!(bg_color: on_magenta => ColorType::Magenta); + def_color!(bg_color: on_dark_magenta => ColorType::DarkMagenta); + def_color!(bg_color: on_cyan => ColorType::Cyan); + def_color!(bg_color: on_dark_cyan => ColorType::DarkCyan); + def_color!(bg_color: on_white => ColorType::White); + def_color!(bg_color: on_grey => ColorType::Grey); } impl Styler for StyledObject { @@ -129,3 +129,32 @@ impl Styler for StyledObject { def_attr!(hidden => Attribute::Hidden); def_attr!(crossed_out => Attribute::CrossedOut); } + +#[cfg(test)] +mod tests { + use crate::{Attribute, ColorType, ObjectStyle}; + + #[test] + fn test_set_fg_bg_add_attr() { + let mut object_style = ObjectStyle::new() + .set_fg(ColorType::Blue) + .set_bg(ColorType::Red); + object_style.add_attr(Attribute::Reset); + + let mut styled_object = object_style.apply_to("test"); + + styled_object = styled_object + .with(ColorType::Green) + .on(ColorType::Magenta) + .attr(Attribute::NoItalic); + + assert_eq!(styled_object.object_style.fg_color, Some(ColorType::Green)); + assert_eq!( + styled_object.object_style.bg_color, + Some(ColorType::Magenta) + ); + assert_eq!(styled_object.object_style.attrs.len(), 2); + assert_eq!(styled_object.object_style.attrs[0], Attribute::Reset); + assert_eq!(styled_object.object_style.attrs[1], Attribute::NoItalic); + } +} diff --git a/src/traits.rs b/src/traits.rs index 6867463..91d0978 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,14 +2,16 @@ use std::fmt::Display; use crate::StyledObject; -/// Provides a set of methods to color any type implementing `Display` with attributes. +/// Provides a set of methods to color any type implementing `Display` + `Clone`. /// -/// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them. +/// This trait has been implemented for `&static str` and `StyledObject`, you can invoke these methods there. /// -/// ```rust +/// # Example +/// +/// ```no_run /// use crossterm_style::Colorize; /// -/// let styled_text = "Red forground color on blue background.".red().on_blue(); +/// let styled_text = "Red foreground color on blue background.".red().on_blue(); /// println!("{}", styled_text); /// ``` pub trait Colorize { @@ -50,11 +52,11 @@ pub trait Colorize { /// Provides a set of methods to style any type implementing `Display` with attributes. /// -/// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them. +/// This trait has been implemented for `&static str` and `StyledObject`, you can invoke these methods there. /// /// # Example /// -/// ```rust +/// ```no_run /// use crossterm_style::Styler; /// /// println!("{}", "Bold text".bold()); From 1ce1fe1c96d5872385ffe9d513017b71d111399a Mon Sep 17 00:00:00 2001 From: Timon Post Date: Wed, 25 Sep 2019 22:05:53 +0200 Subject: [PATCH 02/21] comments, and no_run for example --- src/color.rs | 2 +- src/color/winapi.rs | 2 +- src/lib.rs | 35 ++++++++++++++++------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/color.rs b/src/color.rs index 077d8d0..30ce29e 100644 --- a/src/color.rs +++ b/src/color.rs @@ -16,7 +16,7 @@ use crossterm_utils::Result; /// /// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), /// so that color-related actions can be performed on both UNIX and Windows systems. -pub trait Color { +pub(crate) trait Color : Sync + Send { /// Set the foreground color to the given color. fn set_fg(&self, fg_color: ColorType) -> Result<()>; /// Set the background color to the given color. diff --git a/src/color/winapi.rs b/src/color/winapi.rs index 4b3b123..4df54a5 100644 --- a/src/color/winapi.rs +++ b/src/color/winapi.rs @@ -21,7 +21,7 @@ const BG_BLUE: u16 = wincon::BACKGROUND_BLUE; const BG_INTENSITY: u16 = wincon::BACKGROUND_INTENSITY; /// This struct is a WinApi implementation for color related actions. -pub struct WinApiColor; +pub(crate) struct WinApiColor; impl WinApiColor { pub fn new() -> Box { diff --git a/src/lib.rs b/src/lib.rs index 1ee79e7..299b0f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,51 +179,48 @@ #![deny(unused_imports)] use std::fmt::Display; +use std::env; pub use crossterm_utils::{ execute, impl_display, queue, supports_ansi, Command, ExecutableCommand, QueueableCommand, Result, }; -pub use self::enums::{Attribute, ColorType, Colored}; -pub use self::objectstyle::ObjectStyle; -pub use self::styledobject::StyledObject; -pub use self::traits::{Colorize, Styler}; - use color::ansi::{self, AnsiColor}; #[cfg(windows)] use color::winapi::WinApiColor; use color::Color; -use std::env; + +pub use self::enums::{Attribute, ColorType, Colored}; +pub use self::objectstyle::ObjectStyle; +pub use self::styledobject::StyledObject; +pub use self::traits::{Colorize, Styler}; #[macro_use] mod macros; mod color; mod enums; -pub mod objectstyle; -pub mod styledobject; mod traits; +mod objectstyle; +mod styledobject; /// This could be used to style a type that implements `Display` with colors and attributes. /// /// # Example -/// ```ignore +/// ```no_run /// // get a styled object which could be painted to the terminal. +/// use crossterm_style::{style, ColorType}; +/// /// let styled_object = style("Some Blue colored text on black background") /// .with(ColorType::Blue) /// .on(ColorType::Black); /// -/// // print the styled text * times to the current screen. +/// // print the styled text 10 * times to the current screen. /// for i in 1..10 /// { /// println!("{}", styled_object); /// } /// ``` -/// -/// # Important Remark -/// -/// - Please checkout the documentation for `Colorizer` or `Styler`. -/// Those types will make it a bit easier to style a string. pub fn style<'a, D: 'a>(val: D) -> StyledObject where D: Display + Clone, @@ -293,15 +290,15 @@ impl Styler<&'static str> for &'static str { /// - RGB support (Windows 10 and UNIX only) /// - Text Attributes like: bold, italic, underscore and crossed word ect (Windows 10 and UNIX only) /// -/// Check `/examples/` in the library for more specific examples. +/// Check [examples](https://github.com/crossterm-rs/examples) in the library for more specific examples. /// /// ## Examples /// /// Basic usage: /// /// ```no_run -/// // You can replace the following line with `use crossterm::TerminalCursor;` -/// // if you're using the `crossterm` crate with the `cursor` feature enabled. +/// // You can replace the following line with `use crossterm::TerminalColor;` +/// // if you're using the `crossterm` crate with the `style` feature enabled. /// use crossterm_style::{Result, TerminalColor, ColorType}; /// /// fn main() -> Result<()> { @@ -322,7 +319,7 @@ pub struct TerminalColor { } impl TerminalColor { - /// Create new instance whereon color related actions can be performed. + /// Creates a new `TerminalColor` pub fn new() -> TerminalColor { #[cfg(windows)] let color = if supports_ansi() { From 79730ec289a34b8f2be11b6d78fe3d0cf6f92487 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 13:45:32 +0200 Subject: [PATCH 03/21] added lazy_static, and changed ColorType back to Color --- CHANGELOG.md | 2 +- Cargo.toml | 1 + README.md | 12 +-- src/color.rs | 8 +- src/color/ansi.rs | 66 ++++++++-------- src/color/winapi.rs | 162 ++++++++++++++++++++++------------------ src/enums.rs | 4 +- src/enums/color.rs | 111 +++++++++++++++++++++++++++ src/enums/color_type.rs | 111 --------------------------- src/enums/colored.rs | 12 +-- src/lib.rs | 122 +++++++++++++++--------------- src/objectstyle.rs | 28 +++---- src/styledobject.rs | 87 ++++++++++----------- 13 files changed, 366 insertions(+), 360 deletions(-) create mode 100644 src/enums/color.rs delete mode 100644 src/enums/color_type.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b4253..98d7850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Introduced more `Attributes` - Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). -- Removed `ColorType` since it was unnecessary. +- Removed `Color` since it was unnecessary. # Version 0.1.0 diff --git a/Cargo.toml b/Cargo.toml index 8edb155..85dd0b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ edition = "2018" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.8", features = ["wincon"] } crossterm_winapi = { version = "0.2.1" } +lazy_static = "1.4" [dependencies] crossterm_utils = { version = "0.3.1" } diff --git a/README.md b/README.md index b28c6ec..c96aa7b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ crossterm_style = "0.5" And import the `crossterm_style` modules you want to use. ```rust -pub use crossterm_style::{color, style, Attribute, Color, ColorType, ObjectStyle, StyledObject, TerminalColor, Colorize, Styler}; +pub use crossterm_style::{color, style, Attribute, Color, Color, ObjectStyle, StyledObject, TerminalColor, Colorize, Styler}; ``` ### Useful Links @@ -91,29 +91,29 @@ _style text with colors_ ```rust use crossterm_style::{Colored, Color, Colorize}; -println!("{} Red foreground color", Colored::Fg(ColorType::Red)); -println!("{} Blue background color", Colored::Bg(ColorType::Blue)); +println!("{} Red foreground color", Colored::Fg(Color::Red)); +println!("{} Blue background color", Colored::Bg(Color::Blue)); // you can also call different coloring methods on a `&str`. let styled_text = "Bold Underlined".red().on_blue(); println!("{}", styled_text); // old-way but still usable -let styled_text = style("Bold Underlined").with(ColorType::Red).on(ColorType::Blue); +let styled_text = style("Bold Underlined").with(Color::Red).on(Color::Blue); ``` _style text with RGB and ANSI Value_ ```rust // custom rgb value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(ColorType::Rgb { +println!("{} some colored text", Colored::Fg(Color::Rgb { r: 10, g: 10, b: 10 })); // custom ansi color value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(ColorType::AnsiValue(10))); +println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); ``` ## Tested terminals diff --git a/src/color.rs b/src/color.rs index 30ce29e..c0e2f14 100644 --- a/src/color.rs +++ b/src/color.rs @@ -5,7 +5,7 @@ pub(crate) mod ansi; #[cfg(windows)] pub(crate) mod winapi; -use super::ColorType; +use super::Color; use crossterm_utils::Result; /// This trait defines the actions that can be performed with terminal colors. @@ -16,11 +16,11 @@ use crossterm_utils::Result; /// /// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), /// so that color-related actions can be performed on both UNIX and Windows systems. -pub(crate) trait Color : Sync + Send { +pub(crate) trait Style: Sync + Send { /// Set the foreground color to the given color. - fn set_fg(&self, fg_color: ColorType) -> Result<()>; + fn set_fg(&self, fg_color: Color) -> Result<()>; /// Set the background color to the given color. - fn set_bg(&self, fg_color: ColorType) -> Result<()>; + fn set_bg(&self, fg_color: Color) -> Result<()>; /// Reset the terminal color to default. fn reset(&self) -> Result<()>; } diff --git a/src/color/ansi.rs b/src/color/ansi.rs index 1d6cef5..bba40a7 100644 --- a/src/color/ansi.rs +++ b/src/color/ansi.rs @@ -3,13 +3,13 @@ use crossterm_utils::{csi, write_cout, Result}; -use crate::{Attribute, Color, ColorType, Colored}; +use crate::{Attribute, Color, Colored, Style}; -pub(crate) fn set_fg_sequence(fg_color: ColorType) -> String { +pub(crate) fn set_fg_sequence(fg_color: Color) -> String { format!(csi!("{}m"), color_value(Colored::Fg(fg_color))) } -pub(crate) fn set_bg_csi_sequence(bg_color: ColorType) -> String { +pub(crate) fn set_bg_csi_sequence(bg_color: Color) -> String { format!(csi!("{}m"), color_value(Colored::Bg(bg_color))) } @@ -28,13 +28,13 @@ impl AnsiColor { } } -impl Color for AnsiColor { - fn set_fg(&self, fg_color: ColorType) -> Result<()> { +impl Style for AnsiColor { + fn set_fg(&self, fg_color: Color) -> Result<()> { write_cout!(set_fg_sequence(fg_color))?; Ok(()) } - fn set_bg(&self, bg_color: ColorType) -> Result<()> { + fn set_bg(&self, bg_color: Color) -> Result<()> { write_cout!(set_bg_csi_sequence(bg_color))?; Ok(()) } @@ -52,7 +52,7 @@ fn color_value(colored: Colored) -> String { match colored { Colored::Fg(new_color) => { - if new_color == ColorType::Reset { + if new_color == Color::Reset { ansi_value.push_str("39"); return ansi_value; } else { @@ -61,7 +61,7 @@ fn color_value(colored: Colored) -> String { } } Colored::Bg(new_color) => { - if new_color == ColorType::Reset { + if new_color == Color::Reset { ansi_value.push_str("49"); return ansi_value; } else { @@ -74,27 +74,27 @@ fn color_value(colored: Colored) -> String { let rgb_val: String; let color_val = match color { - ColorType::Black => "5;0", - ColorType::DarkGrey => "5;8", - ColorType::Red => "5;9", - ColorType::DarkRed => "5;1", - ColorType::Green => "5;10", - ColorType::DarkGreen => "5;2", - ColorType::Yellow => "5;11", - ColorType::DarkYellow => "5;3", - ColorType::Blue => "5;12", - ColorType::DarkBlue => "5;4", - ColorType::Magenta => "5;13", - ColorType::DarkMagenta => "5;5", - ColorType::Cyan => "5;14", - ColorType::DarkCyan => "5;6", - ColorType::White => "5;15", - ColorType::Grey => "5;7", - ColorType::Rgb { r, g, b } => { + Color::Black => "5;0", + Color::DarkGrey => "5;8", + Color::Red => "5;9", + Color::DarkRed => "5;1", + Color::Green => "5;10", + Color::DarkGreen => "5;2", + Color::Yellow => "5;11", + Color::DarkYellow => "5;3", + Color::Blue => "5;12", + Color::DarkBlue => "5;4", + Color::Magenta => "5;13", + Color::DarkMagenta => "5;5", + Color::Cyan => "5;14", + Color::DarkCyan => "5;6", + Color::White => "5;15", + Color::Grey => "5;7", + Color::Rgb { r, g, b } => { rgb_val = format!("2;{};{};{}", r, g, b); rgb_val.as_str() } - ColorType::AnsiValue(val) => { + Color::AnsiValue(val) => { rgb_val = format!("5;{}", val); rgb_val.as_str() } @@ -108,41 +108,41 @@ fn color_value(colored: Colored) -> String { #[cfg(test)] mod tests { use crate::color::ansi::color_value; - use crate::{ColorType, Colored}; + use crate::{Color, Colored}; #[test] fn test_parse_fg_color() { - let colored = Colored::Fg(ColorType::Red); + let colored = Colored::Fg(Color::Red); assert_eq!(color_value(colored), "38;5;9"); } #[test] fn test_parse_bg_color() { - let colored = Colored::Bg(ColorType::Red); + let colored = Colored::Bg(Color::Red); assert_eq!(color_value(colored), "48;5;9"); } #[test] fn test_parse_reset_fg_color() { - let colored = Colored::Fg(ColorType::Reset); + let colored = Colored::Fg(Color::Reset); assert_eq!(color_value(colored), "39"); } #[test] fn test_parse_reset_bg_color() { - let colored = Colored::Bg(ColorType::Reset); + let colored = Colored::Bg(Color::Reset); assert_eq!(color_value(colored), "49"); } #[test] fn test_parse_fg_rgb_color() { - let colored = Colored::Bg(ColorType::Rgb { r: 1, g: 2, b: 3 }); + let colored = Colored::Bg(Color::Rgb { r: 1, g: 2, b: 3 }); assert_eq!(color_value(colored), "48;2;1;2;3"); } #[test] fn test_parse_fg_ansi_color() { - let colored = Colored::Fg(ColorType::AnsiValue(255)); + let colored = Colored::Fg(Color::AnsiValue(255)); assert_eq!(color_value(colored), "38;5;255"); } } diff --git a/src/color/winapi.rs b/src/color/winapi.rs index 4df54a5..db664f4 100644 --- a/src/color/winapi.rs +++ b/src/color/winapi.rs @@ -1,14 +1,15 @@ //! This is a `WinApi` specific implementation for styling related action. //! This module is used for non supporting `ANSI` Windows terminals. -use std::sync::Once; - use winapi::um::wincon; use crossterm_utils::Result; use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; +use lazy_static::lazy_static; + +use std::sync::Mutex; -use crate::{Color, ColorType, Colored}; +use crate::{Color, Colored, Style}; const FG_GREEN: u16 = wincon::FOREGROUND_GREEN; const FG_RED: u16 = wincon::FOREGROUND_RED; @@ -25,15 +26,14 @@ pub(crate) struct WinApiColor; impl WinApiColor { pub fn new() -> Box { + init_console_color().unwrap(); + Box::from(WinApiColor) } } -impl Color for WinApiColor { - fn set_fg(&self, fg_color: ColorType) -> Result<()> { - // init the original color in case it is not set. - init_console_color()?; - +impl Style for WinApiColor { + fn set_fg(&self, fg_color: Color) -> Result<()> { let color_value = color_value(Colored::Fg(fg_color)); let screen_buffer = ScreenBuffer::current()?; @@ -57,10 +57,7 @@ impl Color for WinApiColor { Ok(()) } - fn set_bg(&self, bg_color: ColorType) -> Result<()> { - // init the original color in case it is not set. - init_console_color()?; - + fn set_bg(&self, bg_color: Color) -> Result<()> { let color_value = color_value(Colored::Bg(bg_color)); let screen_buffer = ScreenBuffer::current()?; @@ -86,7 +83,9 @@ impl Color for WinApiColor { fn reset(&self) -> Result<()> { // init the original color in case it is not set. + // safe unwrap, initial console color was set with `init_console_color`. let original_color = original_console_color(); + Console::from(Handle::new(HandleType::CurrentOutputHandle)?) .set_text_attribute(original_color)?; @@ -94,77 +93,73 @@ impl Color for WinApiColor { } } -/// This will get the winapi color value from the Color and ColorType struct +/// This will get the winapi color value from the Color and Color struct fn color_value(color: Colored) -> u16 { let winapi_color: u16; match color { Colored::Fg(color) => { winapi_color = match color { - ColorType::Black => 0, - ColorType::DarkGrey => FG_INTENSITY, - ColorType::Red => FG_INTENSITY | FG_RED, - ColorType::DarkRed => FG_RED, - ColorType::Green => FG_INTENSITY | FG_GREEN, - ColorType::DarkGreen => FG_GREEN, - ColorType::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, - ColorType::DarkYellow => FG_GREEN | FG_RED, - ColorType::Blue => FG_INTENSITY | FG_BLUE, - ColorType::DarkBlue => FG_BLUE, - ColorType::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, - ColorType::DarkMagenta => FG_RED | FG_BLUE, - ColorType::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, - ColorType::DarkCyan => FG_GREEN | FG_BLUE, - ColorType::White => FG_RED | FG_GREEN | FG_BLUE, - ColorType::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, - - ColorType::Reset => { - // init the original color in case it is not set. - let mut original_color = original_console_color(); + Color::Black => 0, + Color::DarkGrey => FG_INTENSITY, + Color::Red => FG_INTENSITY | FG_RED, + Color::DarkRed => FG_RED, + Color::Green => FG_INTENSITY | FG_GREEN, + Color::DarkGreen => FG_GREEN, + Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, + Color::DarkYellow => FG_GREEN | FG_RED, + Color::Blue => FG_INTENSITY | FG_BLUE, + Color::DarkBlue => FG_BLUE, + Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, + Color::DarkMagenta => FG_RED | FG_BLUE, + Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, + Color::DarkCyan => FG_GREEN | FG_BLUE, + Color::White => FG_RED | FG_GREEN | FG_BLUE, + Color::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, + + Color::Reset => { + // safe unwrap, initial console color was set with `init_console_color`. + let original_color = original_console_color(); const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; // remove all background values from the original color, we don't want to reset those. - original_color &= !(REMOVE_BG_MASK); - - original_color + (original_color & !(REMOVE_BG_MASK)) } /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - ColorType::Rgb { r: _, g: _, b: _ } => 0, - ColorType::AnsiValue(_val) => 0, + Color::Rgb { r: _, g: _, b: _ } => 0, + Color::AnsiValue(_val) => 0, }; } Colored::Bg(color) => { winapi_color = match color { - ColorType::Black => 0, - ColorType::DarkGrey => BG_INTENSITY, - ColorType::Red => BG_INTENSITY | BG_RED, - ColorType::DarkRed => BG_RED, - ColorType::Green => BG_INTENSITY | BG_GREEN, - ColorType::DarkGreen => BG_GREEN, - ColorType::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, - ColorType::DarkYellow => BG_GREEN | BG_RED, - ColorType::Blue => BG_INTENSITY | BG_BLUE, - ColorType::DarkBlue => BG_BLUE, - ColorType::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, - ColorType::DarkMagenta => BG_RED | BG_BLUE, - ColorType::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, - ColorType::DarkCyan => BG_GREEN | BG_BLUE, - ColorType::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, - ColorType::Grey => BG_RED | BG_GREEN | BG_BLUE, - - ColorType::Reset => { - // init the original color in case it is not set. - let mut original_color = original_console_color(); + Color::Black => 0, + Color::DarkGrey => BG_INTENSITY, + Color::Red => BG_INTENSITY | BG_RED, + Color::DarkRed => BG_RED, + Color::Green => BG_INTENSITY | BG_GREEN, + Color::DarkGreen => BG_GREEN, + Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, + Color::DarkYellow => BG_GREEN | BG_RED, + Color::Blue => BG_INTENSITY | BG_BLUE, + Color::DarkBlue => BG_BLUE, + Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, + Color::DarkMagenta => BG_RED | BG_BLUE, + Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, + Color::DarkCyan => BG_GREEN | BG_BLUE, + Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, + Color::Grey => BG_RED | BG_GREEN | BG_BLUE, + + Color::Reset => { + let original_color = original_console_color(); const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; // remove all foreground values from the original color, we don't want to reset those. - original_color &= !(REMOVE_FG_MASK); - original_color + (original_color & !(REMOVE_FG_MASK)) } /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - ColorType::Rgb { r: _, g: _, b: _ } => 0, - ColorType::AnsiValue(_val) => 0, + Color::Rgb { r: _, g: _, b: _ } => 0, + Color::AnsiValue(_val) => 0, }; } }; @@ -173,37 +168,56 @@ fn color_value(color: Colored) -> u16 { } fn init_console_color() -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; + let mut locked_pos = ORIGINAL_CONSOLE_COLOR.lock().unwrap(); - let attr = screen_buffer.info()?.attributes(); + if *locked_pos == None { + let screen_buffer = ScreenBuffer::current()?; + let attr = screen_buffer.info()?.attributes(); + *locked_pos = Some(attr); + } - GET_ORIGINAL_CONSOLE_COLOR.call_once(|| { - unsafe { ORIGINAL_CONSOLE_COLOR = attr }; - }); Ok(()) } +// returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. fn original_console_color() -> u16 { - return unsafe { ORIGINAL_CONSOLE_COLOR }; + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor` + ORIGINAL_CONSOLE_COLOR + .lock() + .unwrap() + .expect("Initial console color not set") } -static GET_ORIGINAL_CONSOLE_COLOR: Once = Once::new(); -static mut ORIGINAL_CONSOLE_COLOR: u16 = 0; +lazy_static! { + static ref ORIGINAL_CONSOLE_COLOR: Mutex> = Mutex::new(None); +} #[cfg(test)] mod tests { - use crate::color::winapi::{color_value, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED}; - use crate::{ColorType, Colored}; + use super::ORIGINAL_CONSOLE_COLOR; + use crate::color::winapi::{ + color_value, WinApiColor, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, + }; + use crate::{Color, Colored}; #[test] fn test_parse_fg_color() { - let colored = Colored::Fg(ColorType::Red); + let colored = Colored::Fg(Color::Red); assert_eq!(color_value(colored), FG_INTENSITY | FG_RED); } #[test] fn test_parse_bg_color() { - let colored = Colored::Bg(ColorType::Red); + let colored = Colored::Bg(Color::Red); assert_eq!(color_value(colored), BG_INTENSITY | BG_RED); } + + #[test] + fn test_original_console_color_is_set() { + assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_none()); + + let win_color = WinApiColor::new(); + + assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_some()); + } } diff --git a/src/enums.rs b/src/enums.rs index e46e1dd..e790c64 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1,5 +1,5 @@ -pub use self::{attribute::Attribute, color_type::ColorType, colored::Colored}; +pub use self::{attribute::Attribute, color::Color, colored::Colored}; mod attribute; -mod color_type; +mod color; mod colored; diff --git a/src/enums/color.rs b/src/enums/color.rs new file mode 100644 index 0000000..d17303d --- /dev/null +++ b/src/enums/color.rs @@ -0,0 +1,111 @@ +use std::convert::AsRef; +use std::str::FromStr; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Enum with the different colors to color your test and terminal. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum Color { + // This resets the color. + Reset, + + Black, + DarkGrey, + + Red, + DarkRed, + + Green, + DarkGreen, + + Yellow, + DarkYellow, + + Blue, + DarkBlue, + + Magenta, + DarkMagenta, + + Cyan, + DarkCyan, + + White, + Grey, + /// Color representing RGB-colors; + /// r = red + /// g = green + /// b = blue + Rgb { + r: u8, + g: u8, + b: u8, + }, + AnsiValue(u8), +} + +impl FromStr for Color { + type Err = (); + + /// Creates a `Color` from the string representation. + /// + /// # Remarks + /// + /// * `Color::White` is returned in case of an unknown color. + /// * This function does not return `Err` and you can safely unwrap. + fn from_str(src: &str) -> ::std::result::Result { + let src = src.to_lowercase(); + + match src.as_ref() { + "black" => Ok(Color::Black), + "dark_grey" => Ok(Color::DarkGrey), + "red" => Ok(Color::Red), + "dark_red" => Ok(Color::DarkRed), + "green" => Ok(Color::Green), + "dark_green" => Ok(Color::DarkGreen), + "yellow" => Ok(Color::Yellow), + "dark_yellow" => Ok(Color::DarkYellow), + "blue" => Ok(Color::Blue), + "dark_blue" => Ok(Color::DarkBlue), + "magenta" => Ok(Color::Magenta), + "dark_magenta" => Ok(Color::DarkMagenta), + "cyan" => Ok(Color::Cyan), + "dark_cyan" => Ok(Color::DarkCyan), + "white" => Ok(Color::White), + "grey" => Ok(Color::Grey), + _ => Ok(Color::White), + } + } +} + +#[cfg(test)] +mod tests { + use super::Color; + + #[test] + fn test_known_color_conversion() { + assert_eq!("black".parse(), Ok(Color::Black)); + assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); + assert_eq!("red".parse(), Ok(Color::Red)); + assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); + assert_eq!("green".parse(), Ok(Color::Green)); + assert_eq!("dark_green".parse(), Ok(Color::DarkGreen)); + assert_eq!("yellow".parse(), Ok(Color::Yellow)); + assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow)); + assert_eq!("blue".parse(), Ok(Color::Blue)); + assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue)); + assert_eq!("magenta".parse(), Ok(Color::Magenta)); + assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta)); + assert_eq!("cyan".parse(), Ok(Color::Cyan)); + assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); + assert_eq!("white".parse(), Ok(Color::White)); + assert_eq!("grey".parse(), Ok(Color::Grey)); + } + + #[test] + fn test_unknown_color_conversion_yields_white() { + assert_eq!("foo".parse(), Ok(Color::White)); + } +} diff --git a/src/enums/color_type.rs b/src/enums/color_type.rs deleted file mode 100644 index 8fabe17..0000000 --- a/src/enums/color_type.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::convert::AsRef; -use std::str::FromStr; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Enum with the different colors to color your test and terminal. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum ColorType { - // This resets the color. - Reset, - - Black, - DarkGrey, - - Red, - DarkRed, - - Green, - DarkGreen, - - Yellow, - DarkYellow, - - Blue, - DarkBlue, - - Magenta, - DarkMagenta, - - Cyan, - DarkCyan, - - White, - Grey, - /// Color representing RGB-colors; - /// r = red - /// g = green - /// b = blue - Rgb { - r: u8, - g: u8, - b: u8, - }, - AnsiValue(u8), -} - -impl FromStr for ColorType { - type Err = (); - - /// Creates a `Color` from the string representation. - /// - /// # Remarks - /// - /// * `ColorType::White` is returned in case of an unknown color. - /// * This function does not return `Err` and you can safely unwrap. - fn from_str(src: &str) -> ::std::result::Result { - let src = src.to_lowercase(); - - match src.as_ref() { - "black" => Ok(ColorType::Black), - "dark_grey" => Ok(ColorType::DarkGrey), - "red" => Ok(ColorType::Red), - "dark_red" => Ok(ColorType::DarkRed), - "green" => Ok(ColorType::Green), - "dark_green" => Ok(ColorType::DarkGreen), - "yellow" => Ok(ColorType::Yellow), - "dark_yellow" => Ok(ColorType::DarkYellow), - "blue" => Ok(ColorType::Blue), - "dark_blue" => Ok(ColorType::DarkBlue), - "magenta" => Ok(ColorType::Magenta), - "dark_magenta" => Ok(ColorType::DarkMagenta), - "cyan" => Ok(ColorType::Cyan), - "dark_cyan" => Ok(ColorType::DarkCyan), - "white" => Ok(ColorType::White), - "grey" => Ok(ColorType::Grey), - _ => Ok(ColorType::White), - } - } -} - -#[cfg(test)] -mod tests { - use super::ColorType; - - #[test] - fn test_known_color_conversion() { - assert_eq!("black".parse(), Ok(ColorType::Black)); - assert_eq!("dark_grey".parse(), Ok(ColorType::DarkGrey)); - assert_eq!("red".parse(), Ok(ColorType::Red)); - assert_eq!("dark_red".parse(), Ok(ColorType::DarkRed)); - assert_eq!("green".parse(), Ok(ColorType::Green)); - assert_eq!("dark_green".parse(), Ok(ColorType::DarkGreen)); - assert_eq!("yellow".parse(), Ok(ColorType::Yellow)); - assert_eq!("dark_yellow".parse(), Ok(ColorType::DarkYellow)); - assert_eq!("blue".parse(), Ok(ColorType::Blue)); - assert_eq!("dark_blue".parse(), Ok(ColorType::DarkBlue)); - assert_eq!("magenta".parse(), Ok(ColorType::Magenta)); - assert_eq!("dark_magenta".parse(), Ok(ColorType::DarkMagenta)); - assert_eq!("cyan".parse(), Ok(ColorType::Cyan)); - assert_eq!("dark_cyan".parse(), Ok(ColorType::DarkCyan)); - assert_eq!("white".parse(), Ok(ColorType::White)); - assert_eq!("grey".parse(), Ok(ColorType::Grey)); - } - - #[test] - fn test_unknown_color_conversion_yields_white() { - assert_eq!("foo".parse(), Ok(ColorType::White)); - } -} diff --git a/src/enums/colored.rs b/src/enums/colored.rs index e795cf2..e8f88fa 100644 --- a/src/enums/colored.rs +++ b/src/enums/colored.rs @@ -4,7 +4,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; use crate::color; -use crate::enums::ColorType; +use crate::enums::Color; /// Can be used to easily change the front and back ground color /// @@ -13,17 +13,17 @@ use crate::enums::ColorType; /// `Colored` implements `Display` therefore you can use it in any `write` operation. /// /// ```no_run -/// use crossterm_style::{Colored, ColorType}; -/// println!("{} Red foreground color", Colored::Fg(ColorType::Red)); -/// println!("{} Blue background color", Colored::Bg(ColorType::Blue)); +/// use crossterm_style::{Colored, Color}; +/// println!("{} Red foreground color", Colored::Fg(Color::Red)); +/// println!("{} Blue background color", Colored::Bg(Color::Blue)); /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum Colored { /// Use this if you want to change the foreground color - Fg(ColorType), + Fg(Color), /// Use this if you want to change the background color - Bg(ColorType), + Bg(Color), } impl Display for Colored { diff --git a/src/lib.rs b/src/lib.rs index 299b0f2..7d15539 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! //! _setup the basics_ //! ```no_run -//! use crossterm_style::{Colored, ColorType, Attribute, Styler, Colorize}; +//! use crossterm_style::{Colored, Color, Attribute, Styler, Colorize}; //! //! fn main() { //! /* your code here */ @@ -76,9 +76,9 @@ //! //! ### Using Enum //! ```no_run -//! use crossterm_style::{Colored, ColorType}; -//! println!("{} Red foreground color", Colored::Fg(ColorType::Red)); -//! println!("{} Blue background color", Colored::Bg(ColorType::Blue)); +//! use crossterm_style::{Colored, Color}; +//! println!("{} Red foreground color", Colored::Fg(Color::Red)); +//! println!("{} Blue background color", Colored::Bg(Color::Blue)); //! ``` //! `Colored::Bg` will set the background color, and `Colored::Fg` will set the foreground color to the provided color. //! The provided color is of type `Color` and has a bunch of enum values you could choose out. @@ -102,12 +102,12 @@ //! //! ### RGB //! Most UNIX terminals and all Windows 10 consoles are supporting [True color(24-bit)](https://en.wikipedia.org/wiki/Color_depth#True_color_(24-bit)) coloring scheme. -//! You can set the color of the terminal by using `ColorType::RGB(r,g,b)`. +//! You can set the color of the terminal by using `Color::RGB(r,g,b)`. //! //! ```no_run //! // custom rgb value (Windows 10 and UNIX systems) -//! use crossterm_style::{Colored, ColorType}; -//! println!("{}{} 'Light green' text on 'Black' background", Colored::Fg(ColorType::Rgb { r: 0, g: 255, b: 128 }), Colored::Bg(ColorType::Rgb {r: 0, g: 0, b: 0})); +//! use crossterm_style::{Colored, Color}; +//! println!("{}{} 'Light green' text on 'Black' background", Colored::Fg(Color::Rgb { r: 0, g: 255, b: 128 }), Colored::Bg(Color::Rgb {r: 0, g: 0, b: 0})); //! ``` //! This will print some light green text on black background. //! @@ -117,8 +117,8 @@ //! //! ``` //! // custom ansi color value (Windows 10 and UNIX systems) -//! use crossterm_style::{Colored, ColorType}; -//! println!("{} some colored text", Colored::Fg(ColorType::AnsiValue(10))); +//! use crossterm_style::{Colored, Color}; +//! println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); //! ``` //! //! ## Attributes @@ -156,13 +156,13 @@ //! use std::io::{stdout, Write}; //! //! use crossterm_utils::{execute, Result, Output}; -//! use crossterm_style::{SetBg, SetFg, SetAttr, ColorType, Attribute}; +//! use crossterm_style::{SetBg, SetFg, SetAttr, Color, Attribute}; //! //! fn main() -> Result<()> { //! execute!( //! stdout(), -//! SetFg(ColorType::Blue), -//! SetBg(ColorType::Red), +//! SetFg(Color::Blue), +//! SetBg(Color::Red), //! Output("Blue text on red background".to_string()), //! SetAttr(Attribute::Reset) //! ) @@ -178,8 +178,8 @@ #![deny(unused_imports)] -use std::fmt::Display; use std::env; +use std::fmt::Display; pub use crossterm_utils::{ execute, impl_display, queue, supports_ansi, Command, ExecutableCommand, QueueableCommand, @@ -189,9 +189,9 @@ pub use crossterm_utils::{ use color::ansi::{self, AnsiColor}; #[cfg(windows)] use color::winapi::WinApiColor; -use color::Color; +use color::Style; -pub use self::enums::{Attribute, ColorType, Colored}; +pub use self::enums::{Attribute, Color, Colored}; pub use self::objectstyle::ObjectStyle; pub use self::styledobject::StyledObject; pub use self::traits::{Colorize, Styler}; @@ -200,20 +200,20 @@ pub use self::traits::{Colorize, Styler}; mod macros; mod color; mod enums; -mod traits; mod objectstyle; mod styledobject; +mod traits; /// This could be used to style a type that implements `Display` with colors and attributes. /// /// # Example /// ```no_run /// // get a styled object which could be painted to the terminal. -/// use crossterm_style::{style, ColorType}; +/// use crossterm_style::{style, Color}; /// /// let styled_object = style("Some Blue colored text on black background") -/// .with(ColorType::Blue) -/// .on(ColorType::Black); +/// .with(Color::Blue) +/// .on(Color::Black); /// /// // print the styled text 10 * times to the current screen. /// for i in 1..10 @@ -230,40 +230,40 @@ where impl Colorize<&'static str> for &'static str { // foreground colors - def_str_color!(fg_color: black => ColorType::Black); - def_str_color!(fg_color: dark_grey => ColorType::DarkGrey); - def_str_color!(fg_color: red => ColorType::Red); - def_str_color!(fg_color: dark_red => ColorType::DarkRed); - def_str_color!(fg_color: green => ColorType::Green); - def_str_color!(fg_color: dark_green => ColorType::DarkGreen); - def_str_color!(fg_color: yellow => ColorType::Yellow); - def_str_color!(fg_color: dark_yellow => ColorType::DarkYellow); - def_str_color!(fg_color: blue => ColorType::Blue); - def_str_color!(fg_color: dark_blue => ColorType::DarkBlue); - def_str_color!(fg_color: magenta => ColorType::Magenta); - def_str_color!(fg_color: dark_magenta => ColorType::DarkMagenta); - def_str_color!(fg_color: cyan => ColorType::Cyan); - def_str_color!(fg_color: dark_cyan => ColorType::DarkCyan); - def_str_color!(fg_color: white => ColorType::White); - def_str_color!(fg_color: grey => ColorType::Grey); + def_str_color!(fg_color: black => Color::Black); + def_str_color!(fg_color: dark_grey => Color::DarkGrey); + def_str_color!(fg_color: red => Color::Red); + def_str_color!(fg_color: dark_red => Color::DarkRed); + def_str_color!(fg_color: green => Color::Green); + def_str_color!(fg_color: dark_green => Color::DarkGreen); + def_str_color!(fg_color: yellow => Color::Yellow); + def_str_color!(fg_color: dark_yellow => Color::DarkYellow); + def_str_color!(fg_color: blue => Color::Blue); + def_str_color!(fg_color: dark_blue => Color::DarkBlue); + def_str_color!(fg_color: magenta => Color::Magenta); + def_str_color!(fg_color: dark_magenta => Color::DarkMagenta); + def_str_color!(fg_color: cyan => Color::Cyan); + def_str_color!(fg_color: dark_cyan => Color::DarkCyan); + def_str_color!(fg_color: white => Color::White); + def_str_color!(fg_color: grey => Color::Grey); // background colors - def_str_color!(bg_color: on_black => ColorType::Black); - def_str_color!(bg_color: on_dark_grey => ColorType::DarkGrey); - def_str_color!(bg_color: on_red => ColorType::Red); - def_str_color!(bg_color: on_dark_red => ColorType::DarkRed); - def_str_color!(bg_color: on_green => ColorType::Green); - def_str_color!(bg_color: on_dark_green => ColorType::DarkGreen); - def_str_color!(bg_color: on_yellow => ColorType::Yellow); - def_str_color!(bg_color: on_dark_yellow => ColorType::DarkYellow); - def_str_color!(bg_color: on_blue => ColorType::Blue); - def_str_color!(bg_color: on_dark_blue => ColorType::DarkBlue); - def_str_color!(bg_color: on_magenta => ColorType::Magenta); - def_str_color!(bg_color: on_dark_magenta => ColorType::DarkMagenta); - def_str_color!(bg_color: on_cyan => ColorType::Cyan); - def_str_color!(bg_color: on_dark_cyan => ColorType::DarkCyan); - def_str_color!(bg_color: on_white => ColorType::White); - def_str_color!(bg_color: on_grey => ColorType::Grey); + def_str_color!(bg_color: on_black => Color::Black); + def_str_color!(bg_color: on_dark_grey => Color::DarkGrey); + def_str_color!(bg_color: on_red => Color::Red); + def_str_color!(bg_color: on_dark_red => Color::DarkRed); + def_str_color!(bg_color: on_green => Color::Green); + def_str_color!(bg_color: on_dark_green => Color::DarkGreen); + def_str_color!(bg_color: on_yellow => Color::Yellow); + def_str_color!(bg_color: on_dark_yellow => Color::DarkYellow); + def_str_color!(bg_color: on_blue => Color::Blue); + def_str_color!(bg_color: on_dark_blue => Color::DarkBlue); + def_str_color!(bg_color: on_magenta => Color::Magenta); + def_str_color!(bg_color: on_dark_magenta => Color::DarkMagenta); + def_str_color!(bg_color: on_cyan => Color::Cyan); + def_str_color!(bg_color: on_dark_cyan => Color::DarkCyan); + def_str_color!(bg_color: on_white => Color::White); + def_str_color!(bg_color: on_grey => Color::Grey); } impl Styler<&'static str> for &'static str { @@ -299,21 +299,21 @@ impl Styler<&'static str> for &'static str { /// ```no_run /// // You can replace the following line with `use crossterm::TerminalColor;` /// // if you're using the `crossterm` crate with the `style` feature enabled. -/// use crossterm_style::{Result, TerminalColor, ColorType}; +/// use crossterm_style::{Result, TerminalColor, Color}; /// /// fn main() -> Result<()> { /// let color = TerminalColor::new(); /// // set foreground color -/// color.set_fg(ColorType::Blue)?; +/// color.set_fg(Color::Blue)?; /// // set background color -/// color.set_bg(ColorType::Red)?; +/// color.set_bg(Color::Red)?; /// // reset to the default colors /// color.reset() /// } /// ``` pub struct TerminalColor { #[cfg(windows)] - color: Box<(dyn Color + Sync + Send)>, + color: Box<(dyn Style + Sync + Send)>, #[cfg(unix)] color: AnsiColor, } @@ -323,9 +323,9 @@ impl TerminalColor { pub fn new() -> TerminalColor { #[cfg(windows)] let color = if supports_ansi() { - Box::from(AnsiColor::new()) as Box<(dyn Color + Sync + Send)> + Box::from(AnsiColor::new()) as Box<(dyn Style + Sync + Send)> } else { - WinApiColor::new() as Box<(dyn Color + Sync + Send)> + WinApiColor::new() as Box<(dyn Style + Sync + Send)> }; #[cfg(unix)] @@ -335,12 +335,12 @@ impl TerminalColor { } /// Set the foreground color to the given color. - pub fn set_fg(&self, color: ColorType) -> Result<()> { + pub fn set_fg(&self, color: Color) -> Result<()> { self.color.set_fg(color) } /// Set the background color to the given color. - pub fn set_bg(&self, color: ColorType) -> Result<()> { + pub fn set_bg(&self, color: Color) -> Result<()> { self.color.set_bg(color) } @@ -369,7 +369,7 @@ pub fn color() -> TerminalColor { /// When executed, this command will set the foreground color of the terminal to the given color. /// /// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetFg(pub ColorType); +pub struct SetFg(pub Color); impl Command for SetFg { type AnsiType = String; @@ -387,7 +387,7 @@ impl Command for SetFg { /// When executed, this command will set the background color of the terminal to the given color. /// /// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetBg(pub ColorType); +pub struct SetBg(pub Color); impl Command for SetBg { type AnsiType = String; diff --git a/src/objectstyle.rs b/src/objectstyle.rs index 767ff83..28df7d2 100644 --- a/src/objectstyle.rs +++ b/src/objectstyle.rs @@ -2,13 +2,13 @@ use std::fmt::Display; -use super::{Attribute, ColorType, StyledObject}; +use super::{Attribute, Color, StyledObject}; /// Struct that contains the style properties that can be applied to a displayable object. #[derive(Debug, Clone, Default)] pub struct ObjectStyle { - pub fg_color: Option, - pub bg_color: Option, + pub fg_color: Option, + pub bg_color: Option, pub attrs: Vec, } @@ -27,13 +27,13 @@ impl ObjectStyle { } /// Set the background color of `ObjectStyle` to the passed color. - pub fn set_bg(mut self, color: ColorType) -> ObjectStyle { + pub fn set_bg(mut self, color: Color) -> ObjectStyle { self.bg_color = Some(color); self } /// Set the foreground color of `ObjectStyle` to the passed color. - pub fn set_fg(mut self, color: ColorType) -> ObjectStyle { + pub fn set_fg(mut self, color: Color) -> ObjectStyle { self.fg_color = Some(color); self } @@ -46,31 +46,27 @@ impl ObjectStyle { #[cfg(test)] mod tests { - use crate::{Attribute, ColorType, ObjectStyle}; + use crate::{Attribute, Color, ObjectStyle}; #[test] fn test_set_fg_bg_add_attr() { - let mut object_style = ObjectStyle::new() - .set_fg(ColorType::Blue) - .set_bg(ColorType::Red); + let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); object_style.add_attr(Attribute::Reset); - assert_eq!(object_style.fg_color, Some(ColorType::Blue)); - assert_eq!(object_style.bg_color, Some(ColorType::Red)); + assert_eq!(object_style.fg_color, Some(Color::Blue)); + assert_eq!(object_style.bg_color, Some(Color::Red)); assert_eq!(object_style.attrs[0], Attribute::Reset); } #[test] fn test_apply_object_style_to_text() { - let mut object_style = ObjectStyle::new() - .set_fg(ColorType::Blue) - .set_bg(ColorType::Red); + let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); object_style.add_attr(Attribute::Reset); let styled_object = object_style.apply_to("test"); - assert_eq!(styled_object.object_style.fg_color, Some(ColorType::Blue)); - assert_eq!(styled_object.object_style.bg_color, Some(ColorType::Red)); + assert_eq!(styled_object.object_style.fg_color, Some(Color::Blue)); + assert_eq!(styled_object.object_style.bg_color, Some(Color::Red)); assert_eq!(styled_object.object_style.attrs[0], Attribute::Reset); } } diff --git a/src/styledobject.rs b/src/styledobject.rs index 6f4522a..f28339a 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -5,7 +5,7 @@ use std::result; use crossterm_utils::{csi, queue}; -use super::{color, Attribute, ColorType, Colorize, ObjectStyle, SetBg, SetFg, Styler}; +use super::{color, Attribute, Color, Colorize, ObjectStyle, SetBg, SetFg, Styler}; /// Contains both the style and the content which can be styled. #[derive(Clone)] @@ -21,7 +21,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// /// This methods consumes 'self', and works like a builder. /// You can do: `with().on().attr()` - pub fn with(mut self, foreground_color: ColorType) -> StyledObject { + pub fn with(mut self, foreground_color: Color) -> StyledObject { self.object_style = self.object_style.set_fg(foreground_color); self } @@ -32,7 +32,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// /// This methods consumes 'self', and works like a builder. /// You can do: `with().on().attr()` - pub fn on(mut self, background_color: ColorType) -> StyledObject { + pub fn on(mut self, background_color: Color) -> StyledObject { self.object_style = self.object_style.set_bg(background_color); self } @@ -80,40 +80,40 @@ impl Display for StyledObject { impl Colorize for StyledObject { // foreground colors - def_color!(fg_color: black => ColorType::Black); - def_color!(fg_color: dark_grey => ColorType::DarkGrey); - def_color!(fg_color: red => ColorType::Red); - def_color!(fg_color: dark_red => ColorType::DarkRed); - def_color!(fg_color: green => ColorType::Green); - def_color!(fg_color: dark_green => ColorType::DarkGreen); - def_color!(fg_color: yellow => ColorType::Yellow); - def_color!(fg_color: dark_yellow => ColorType::DarkYellow); - def_color!(fg_color: blue => ColorType::Blue); - def_color!(fg_color: dark_blue => ColorType::DarkBlue); - def_color!(fg_color: magenta => ColorType::Magenta); - def_color!(fg_color: dark_magenta => ColorType::DarkMagenta); - def_color!(fg_color: cyan => ColorType::Cyan); - def_color!(fg_color: dark_cyan => ColorType::DarkCyan); - def_color!(fg_color: white => ColorType::White); - def_color!(fg_color: grey => ColorType::Grey); + def_color!(fg_color: black => Color::Black); + def_color!(fg_color: dark_grey => Color::DarkGrey); + def_color!(fg_color: red => Color::Red); + def_color!(fg_color: dark_red => Color::DarkRed); + def_color!(fg_color: green => Color::Green); + def_color!(fg_color: dark_green => Color::DarkGreen); + def_color!(fg_color: yellow => Color::Yellow); + def_color!(fg_color: dark_yellow => Color::DarkYellow); + def_color!(fg_color: blue => Color::Blue); + def_color!(fg_color: dark_blue => Color::DarkBlue); + def_color!(fg_color: magenta => Color::Magenta); + def_color!(fg_color: dark_magenta => Color::DarkMagenta); + def_color!(fg_color: cyan => Color::Cyan); + def_color!(fg_color: dark_cyan => Color::DarkCyan); + def_color!(fg_color: white => Color::White); + def_color!(fg_color: grey => Color::Grey); // background colors - def_color!(bg_color: on_black => ColorType::Black); - def_color!(bg_color: on_dark_grey => ColorType::DarkGrey); - def_color!(bg_color: on_red => ColorType::Red); - def_color!(bg_color: on_dark_red => ColorType::DarkRed); - def_color!(bg_color: on_green => ColorType::Green); - def_color!(bg_color: on_dark_green => ColorType::DarkGreen); - def_color!(bg_color: on_yellow => ColorType::Yellow); - def_color!(bg_color: on_dark_yellow => ColorType::DarkYellow); - def_color!(bg_color: on_blue => ColorType::Blue); - def_color!(bg_color: on_dark_blue => ColorType::DarkBlue); - def_color!(bg_color: on_magenta => ColorType::Magenta); - def_color!(bg_color: on_dark_magenta => ColorType::DarkMagenta); - def_color!(bg_color: on_cyan => ColorType::Cyan); - def_color!(bg_color: on_dark_cyan => ColorType::DarkCyan); - def_color!(bg_color: on_white => ColorType::White); - def_color!(bg_color: on_grey => ColorType::Grey); + def_color!(bg_color: on_black => Color::Black); + def_color!(bg_color: on_dark_grey => Color::DarkGrey); + def_color!(bg_color: on_red => Color::Red); + def_color!(bg_color: on_dark_red => Color::DarkRed); + def_color!(bg_color: on_green => Color::Green); + def_color!(bg_color: on_dark_green => Color::DarkGreen); + def_color!(bg_color: on_yellow => Color::Yellow); + def_color!(bg_color: on_dark_yellow => Color::DarkYellow); + def_color!(bg_color: on_blue => Color::Blue); + def_color!(bg_color: on_dark_blue => Color::DarkBlue); + def_color!(bg_color: on_magenta => Color::Magenta); + def_color!(bg_color: on_dark_magenta => Color::DarkMagenta); + def_color!(bg_color: on_cyan => Color::Cyan); + def_color!(bg_color: on_dark_cyan => Color::DarkCyan); + def_color!(bg_color: on_white => Color::White); + def_color!(bg_color: on_grey => Color::Grey); } impl Styler for StyledObject { @@ -132,27 +132,22 @@ impl Styler for StyledObject { #[cfg(test)] mod tests { - use crate::{Attribute, ColorType, ObjectStyle}; + use crate::{Attribute, Color, ObjectStyle}; #[test] fn test_set_fg_bg_add_attr() { - let mut object_style = ObjectStyle::new() - .set_fg(ColorType::Blue) - .set_bg(ColorType::Red); + let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); object_style.add_attr(Attribute::Reset); let mut styled_object = object_style.apply_to("test"); styled_object = styled_object - .with(ColorType::Green) - .on(ColorType::Magenta) + .with(Color::Green) + .on(Color::Magenta) .attr(Attribute::NoItalic); - assert_eq!(styled_object.object_style.fg_color, Some(ColorType::Green)); - assert_eq!( - styled_object.object_style.bg_color, - Some(ColorType::Magenta) - ); + assert_eq!(styled_object.object_style.fg_color, Some(Color::Green)); + assert_eq!(styled_object.object_style.bg_color, Some(Color::Magenta)); assert_eq!(styled_object.object_style.attrs.len(), 2); assert_eq!(styled_object.object_style.attrs[0], Attribute::Reset); assert_eq!(styled_object.object_style.attrs[1], Attribute::NoItalic); From 33d4689a57c7a0d6decb43fcca67b02104a772eb Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 16:28:00 +0200 Subject: [PATCH 04/21] color -> style --- src/lib.rs | 8 ++++---- src/{color.rs => style.rs} | 0 src/{color => style}/ansi.rs | 0 src/{color => style}/winapi.rs | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{color.rs => style.rs} (100%) rename src/{color => style}/ansi.rs (100%) rename src/{color => style}/winapi.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index 7d15539..ac1849b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,10 +186,10 @@ pub use crossterm_utils::{ Result, }; -use color::ansi::{self, AnsiColor}; +use style::ansi::{self, AnsiColor}; #[cfg(windows)] -use color::winapi::WinApiColor; -use color::Style; +use style::winapi::WinApiColor; +use style::Style; pub use self::enums::{Attribute, Color, Colored}; pub use self::objectstyle::ObjectStyle; @@ -198,7 +198,7 @@ pub use self::traits::{Colorize, Styler}; #[macro_use] mod macros; -mod color; +mod style; mod enums; mod objectstyle; mod styledobject; diff --git a/src/color.rs b/src/style.rs similarity index 100% rename from src/color.rs rename to src/style.rs diff --git a/src/color/ansi.rs b/src/style/ansi.rs similarity index 100% rename from src/color/ansi.rs rename to src/style/ansi.rs diff --git a/src/color/winapi.rs b/src/style/winapi.rs similarity index 100% rename from src/color/winapi.rs rename to src/style/winapi.rs From 93606ccf56c3fa2a43a68238f39571e35dfa92d1 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 20:57:07 +0200 Subject: [PATCH 05/21] fixed compile errors --- .travis.yml | 8 +++++++- src/style/ansi.rs | 2 +- src/style/winapi.rs | 8 +++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 978cc69..f7f9203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,9 @@ +# Build only pushed (merged) master or any pull request. This avoids the +# pull request to be build twice. +branches: + only: + - master + language: rust rust: @@ -27,4 +33,4 @@ script: - rustc --version - if [ "$TRAVIS_RUST_VERSION" = "stable" ]; then cargo fmt --all -- --check; fi - cargo build - - cargo test --all-features -- --nocapture --test-threads 1 + - cargo test --all-features -- --nocapture --test-threads 1 \ No newline at end of file diff --git a/src/style/ansi.rs b/src/style/ansi.rs index bba40a7..b588779 100644 --- a/src/style/ansi.rs +++ b/src/style/ansi.rs @@ -107,7 +107,7 @@ fn color_value(colored: Colored) -> String { #[cfg(test)] mod tests { - use crate::color::ansi::color_value; + use crate::style::ansi::color_value; use crate::{Color, Colored}; #[test] diff --git a/src/style/winapi.rs b/src/style/winapi.rs index db664f4..f0865e2 100644 --- a/src/style/winapi.rs +++ b/src/style/winapi.rs @@ -167,6 +167,7 @@ fn color_value(color: Colored) -> u16 { winapi_color } +/// Initializes the default console color. It will will be skipped if it has already been initialized. fn init_console_color() -> Result<()> { let mut locked_pos = ORIGINAL_CONSOLE_COLOR.lock().unwrap(); @@ -179,7 +180,7 @@ fn init_console_color() -> Result<()> { Ok(()) } -// returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. +/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. fn original_console_color() -> u16 { // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor` ORIGINAL_CONSOLE_COLOR @@ -195,7 +196,7 @@ lazy_static! { #[cfg(test)] mod tests { use super::ORIGINAL_CONSOLE_COLOR; - use crate::color::winapi::{ + use crate::style::winapi::{ color_value, WinApiColor, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, }; use crate::{Color, Colored}; @@ -216,7 +217,8 @@ mod tests { fn test_original_console_color_is_set() { assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_none()); - let win_color = WinApiColor::new(); + // will call `init_console_color` + let _ = WinApiColor::new(); assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_some()); } From f37fd0baa2d41c3166e4445e64dfe721cf63deb2 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 22:19:32 +0200 Subject: [PATCH 06/21] cfg windows --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ac1849b..cd88342 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,8 +181,10 @@ use std::env; use std::fmt::Display; +#[cfg(windows)] +use crossterm_utils::supports_ansi; pub use crossterm_utils::{ - execute, impl_display, queue, supports_ansi, Command, ExecutableCommand, QueueableCommand, + execute, impl_display, queue, Command, ExecutableCommand, QueueableCommand, Result, }; From a8722e01731fa35ca97f1648beb38bb63b367dc0 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 22:22:35 +0200 Subject: [PATCH 07/21] fmt --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cd88342..8976c0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,8 +184,7 @@ use std::fmt::Display; #[cfg(windows)] use crossterm_utils::supports_ansi; pub use crossterm_utils::{ - execute, impl_display, queue, Command, ExecutableCommand, QueueableCommand, - Result, + execute, impl_display, queue, Command, ExecutableCommand, QueueableCommand, Result, }; use style::ansi::{self, AnsiColor}; @@ -200,9 +199,9 @@ pub use self::traits::{Colorize, Styler}; #[macro_use] mod macros; -mod style; mod enums; mod objectstyle; +mod style; mod styledobject; mod traits; From 42e68474eeeeb9a11b7176045274b7ae3df118b3 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 14:44:12 +0200 Subject: [PATCH 08/21] round 1 --- CHANGELOG.md | 2 +- README.md | 2 +- src/objectstyle.rs | 8 ++++---- src/style.rs | 7 ++++--- src/style/winapi.rs | 11 ++++++----- src/styledobject.rs | 6 +++--- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d7850..79b4253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Introduced more `Attributes` - Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). -- Removed `Color` since it was unnecessary. +- Removed `ColorType` since it was unnecessary. # Version 0.1.0 diff --git a/README.md b/README.md index c96aa7b..2025a33 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ crossterm_style = "0.5" And import the `crossterm_style` modules you want to use. ```rust -pub use crossterm_style::{color, style, Attribute, Color, Color, ObjectStyle, StyledObject, TerminalColor, Colorize, Styler}; +pub use crossterm_style::{color, style, Attribute, Color, ObjectStyle, StyledObject, TerminalColor, Colorize, Styler}; ``` ### Useful Links diff --git a/src/objectstyle.rs b/src/objectstyle.rs index 28df7d2..c725d1b 100644 --- a/src/objectstyle.rs +++ b/src/objectstyle.rs @@ -27,13 +27,13 @@ impl ObjectStyle { } /// Set the background color of `ObjectStyle` to the passed color. - pub fn set_bg(mut self, color: Color) -> ObjectStyle { + pub fn bg(mut self, color: Color) -> ObjectStyle { self.bg_color = Some(color); self } /// Set the foreground color of `ObjectStyle` to the passed color. - pub fn set_fg(mut self, color: Color) -> ObjectStyle { + pub fn fg(mut self, color: Color) -> ObjectStyle { self.fg_color = Some(color); self } @@ -50,7 +50,7 @@ mod tests { #[test] fn test_set_fg_bg_add_attr() { - let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); + let mut object_style = ObjectStyle::new().fg(Color::Blue).bg(Color::Red); object_style.add_attr(Attribute::Reset); assert_eq!(object_style.fg_color, Some(Color::Blue)); @@ -60,7 +60,7 @@ mod tests { #[test] fn test_apply_object_style_to_text() { - let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); + let mut object_style = ObjectStyle::new().fg(Color::Blue).bg(Color::Red); object_style.add_attr(Attribute::Reset); let styled_object = object_style.apply_to("test"); diff --git a/src/style.rs b/src/style.rs index c0e2f14..10762e9 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,13 +1,14 @@ //! A module that contains all the actions related to the styling of the terminal. //! Like applying attributes to text and changing the foreground and background. +use crossterm_utils::Result; + +use super::Color; + pub(crate) mod ansi; #[cfg(windows)] pub(crate) mod winapi; -use super::Color; -use crossterm_utils::Result; - /// This trait defines the actions that can be performed with terminal colors. /// This trait can be implemented so that a concrete implementation of the ITerminalColor can fulfill /// the wishes to work on a specific platform. diff --git a/src/style/winapi.rs b/src/style/winapi.rs index f0865e2..e461137 100644 --- a/src/style/winapi.rs +++ b/src/style/winapi.rs @@ -1,13 +1,13 @@ //! This is a `WinApi` specific implementation for styling related action. //! This module is used for non supporting `ANSI` Windows terminals. -use winapi::um::wincon; +use std::sync::Mutex; use crossterm_utils::Result; use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; -use lazy_static::lazy_static; +use winapi::um::wincon; -use std::sync::Mutex; +use lazy_static::lazy_static; use crate::{Color, Colored, Style}; @@ -171,7 +171,7 @@ fn color_value(color: Colored) -> u16 { fn init_console_color() -> Result<()> { let mut locked_pos = ORIGINAL_CONSOLE_COLOR.lock().unwrap(); - if *locked_pos == None { + if locked_pos.is_none() { let screen_buffer = ScreenBuffer::current()?; let attr = screen_buffer.info()?.attributes(); *locked_pos = Some(attr); @@ -195,12 +195,13 @@ lazy_static! { #[cfg(test)] mod tests { - use super::ORIGINAL_CONSOLE_COLOR; use crate::style::winapi::{ color_value, WinApiColor, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, }; use crate::{Color, Colored}; + use super::ORIGINAL_CONSOLE_COLOR; + #[test] fn test_parse_fg_color() { let colored = Colored::Fg(Color::Red); diff --git a/src/styledobject.rs b/src/styledobject.rs index f28339a..7cdb2e8 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -22,7 +22,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// This methods consumes 'self', and works like a builder. /// You can do: `with().on().attr()` pub fn with(mut self, foreground_color: Color) -> StyledObject { - self.object_style = self.object_style.set_fg(foreground_color); + self.object_style = self.object_style.fg(foreground_color); self } @@ -33,7 +33,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// This methods consumes 'self', and works like a builder. /// You can do: `with().on().attr()` pub fn on(mut self, background_color: Color) -> StyledObject { - self.object_style = self.object_style.set_bg(background_color); + self.object_style = self.object_style.bg(background_color); self } @@ -136,7 +136,7 @@ mod tests { #[test] fn test_set_fg_bg_add_attr() { - let mut object_style = ObjectStyle::new().set_fg(Color::Blue).set_bg(Color::Red); + let mut object_style = ObjectStyle::new().fg(Color::Blue).bg(Color::Red); object_style.add_attr(Attribute::Reset); let mut styled_object = object_style.apply_to("test"); From bc3c4570304cf27e6e4151225faa206b7cb9483d Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 14:44:59 +0200 Subject: [PATCH 09/21] new line --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f7f9203..179f097 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,4 +33,4 @@ script: - rustc --version - if [ "$TRAVIS_RUST_VERSION" = "stable" ]; then cargo fmt --all -- --check; fi - cargo build - - cargo test --all-features -- --nocapture --test-threads 1 \ No newline at end of file + - cargo test --all-features -- --nocapture --test-threads 1 From a49ca12578fa2b176f0efdfa189c522f13ef95b7 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 15:05:06 +0200 Subject: [PATCH 10/21] color_value -> Into --- CHANGELOG.md | 4 ++ src/style/ansi.rs | 123 ++++++++++++++++++----------------- src/style/winapi.rs | 152 +++++++++++++++++++++----------------------- 3 files changed, 138 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b4253..65e5d9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version master +- Added unit tests +- Restructured files + # Version 0.5.1 - Maintenance release only diff --git a/src/style/ansi.rs b/src/style/ansi.rs index b588779..c5184e5 100644 --- a/src/style/ansi.rs +++ b/src/style/ansi.rs @@ -6,11 +6,11 @@ use crossterm_utils::{csi, write_cout, Result}; use crate::{Attribute, Color, Colored, Style}; pub(crate) fn set_fg_sequence(fg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Fg(fg_color))) + format!(csi!("{}m"), Into::::into(Colored::Fg(fg_color))) } pub(crate) fn set_bg_csi_sequence(bg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Bg(bg_color))) + format!(csi!("{}m"), Into::::into(Colored::Bg(bg_color))) } pub(crate) fn set_attr_csi_sequence(attribute: Attribute) -> String { @@ -45,104 +45,103 @@ impl Style for AnsiColor { } } -fn color_value(colored: Colored) -> String { - let mut ansi_value = String::new(); - - let color; - - match colored { - Colored::Fg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("39"); - return ansi_value; - } else { - ansi_value.push_str("38;"); - color = new_color; +impl Into for Colored { + fn into(self) -> String { + let mut ansi_value = String::new(); + + let color; + + match self { + Colored::Fg(new_color) => { + if new_color == Color::Reset { + ansi_value.push_str("39"); + return ansi_value; + } else { + ansi_value.push_str("38;"); + color = new_color; + } } - } - Colored::Bg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("49"); - return ansi_value; - } else { - ansi_value.push_str("48;"); - color = new_color; + Colored::Bg(new_color) => { + if new_color == Color::Reset { + ansi_value.push_str("49"); + return ansi_value; + } else { + ansi_value.push_str("48;"); + color = new_color; + } } } - } - let rgb_val: String; - - let color_val = match color { - Color::Black => "5;0", - Color::DarkGrey => "5;8", - Color::Red => "5;9", - Color::DarkRed => "5;1", - Color::Green => "5;10", - Color::DarkGreen => "5;2", - Color::Yellow => "5;11", - Color::DarkYellow => "5;3", - Color::Blue => "5;12", - Color::DarkBlue => "5;4", - Color::Magenta => "5;13", - Color::DarkMagenta => "5;5", - Color::Cyan => "5;14", - Color::DarkCyan => "5;6", - Color::White => "5;15", - Color::Grey => "5;7", - Color::Rgb { r, g, b } => { - rgb_val = format!("2;{};{};{}", r, g, b); - rgb_val.as_str() - } - Color::AnsiValue(val) => { - rgb_val = format!("5;{}", val); - rgb_val.as_str() - } - _ => "", - }; + let color_val = match color { + Color::Black => "5;0", + Color::DarkGrey => "5;8", + Color::Red => "5;9", + Color::DarkRed => "5;1", + Color::Green => "5;10", + Color::DarkGreen => "5;2", + Color::Yellow => "5;11", + Color::DarkYellow => "5;3", + Color::Blue => "5;12", + Color::DarkBlue => "5;4", + Color::Magenta => "5;13", + Color::DarkMagenta => "5;5", + Color::Cyan => "5;14", + Color::DarkCyan => "5;6", + Color::White => "5;15", + Color::Grey => "5;7", + Color::Rgb { r, g, b } => { + ansi_value.push_str(format!("2;{};{};{}", r, g, b).as_str()); + "" + } + Color::AnsiValue(val) => { + ansi_value.push_str(format!("5;{}", val).as_str()); + "" + } + _ => "", + }; - ansi_value.push_str(color_val); - ansi_value + ansi_value.push_str(color_val); + ansi_value + } } #[cfg(test)] mod tests { - use crate::style::ansi::color_value; use crate::{Color, Colored}; #[test] fn test_parse_fg_color() { let colored = Colored::Fg(Color::Red); - assert_eq!(color_value(colored), "38;5;9"); + assert_eq!(Into::::into(colored), "38;5;9"); } #[test] fn test_parse_bg_color() { let colored = Colored::Bg(Color::Red); - assert_eq!(color_value(colored), "48;5;9"); + assert_eq!(Into::::into(colored), "48;5;9"); } #[test] fn test_parse_reset_fg_color() { let colored = Colored::Fg(Color::Reset); - assert_eq!(color_value(colored), "39"); + assert_eq!(Into::::into(colored), "39"); } #[test] fn test_parse_reset_bg_color() { let colored = Colored::Bg(Color::Reset); - assert_eq!(color_value(colored), "49"); + assert_eq!(Into::::into(colored), "49"); } #[test] fn test_parse_fg_rgb_color() { let colored = Colored::Bg(Color::Rgb { r: 1, g: 2, b: 3 }); - assert_eq!(color_value(colored), "48;2;1;2;3"); + assert_eq!(Into::::into(colored), "48;2;1;2;3"); } #[test] fn test_parse_fg_ansi_color() { let colored = Colored::Fg(Color::AnsiValue(255)); - assert_eq!(color_value(colored), "38;5;255"); + assert_eq!(Into::::into(colored), "38;5;255"); } } diff --git a/src/style/winapi.rs b/src/style/winapi.rs index e461137..356af26 100644 --- a/src/style/winapi.rs +++ b/src/style/winapi.rs @@ -34,7 +34,7 @@ impl WinApiColor { impl Style for WinApiColor { fn set_fg(&self, fg_color: Color) -> Result<()> { - let color_value = color_value(Colored::Fg(fg_color)); + let color_value: u16 = Colored::Fg(fg_color).into(); let screen_buffer = ScreenBuffer::current()?; let csbi = screen_buffer.info()?; @@ -58,7 +58,7 @@ impl Style for WinApiColor { } fn set_bg(&self, bg_color: Color) -> Result<()> { - let color_value = color_value(Colored::Bg(bg_color)); + let color_value: u16 = Colored::Bg(bg_color).into(); let screen_buffer = ScreenBuffer::current()?; let csbi = screen_buffer.info()?; @@ -82,8 +82,6 @@ impl Style for WinApiColor { } fn reset(&self) -> Result<()> { - // init the original color in case it is not set. - // safe unwrap, initial console color was set with `init_console_color`. let original_color = original_console_color(); Console::from(Handle::new(HandleType::CurrentOutputHandle)?) @@ -93,78 +91,76 @@ impl Style for WinApiColor { } } -/// This will get the winapi color value from the Color and Color struct -fn color_value(color: Colored) -> u16 { - let winapi_color: u16; - - match color { - Colored::Fg(color) => { - winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => FG_INTENSITY, - Color::Red => FG_INTENSITY | FG_RED, - Color::DarkRed => FG_RED, - Color::Green => FG_INTENSITY | FG_GREEN, - Color::DarkGreen => FG_GREEN, - Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, - Color::DarkYellow => FG_GREEN | FG_RED, - Color::Blue => FG_INTENSITY | FG_BLUE, - Color::DarkBlue => FG_BLUE, - Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, - Color::DarkMagenta => FG_RED | FG_BLUE, - Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, - Color::DarkCyan => FG_GREEN | FG_BLUE, - Color::White => FG_RED | FG_GREEN | FG_BLUE, - Color::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, - - Color::Reset => { - // safe unwrap, initial console color was set with `init_console_color`. - let original_color = original_console_color(); - - const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; - // remove all background values from the original color, we don't want to reset those. - (original_color & !(REMOVE_BG_MASK)) +impl Into for Colored { + /// Returns the WinApi color value (u16) from the `Colored` struct. + fn into(self) -> u16 { + match self { + Colored::Fg(color) => { + match color { + Color::Black => 0, + Color::DarkGrey => FG_INTENSITY, + Color::Red => FG_INTENSITY | FG_RED, + Color::DarkRed => FG_RED, + Color::Green => FG_INTENSITY | FG_GREEN, + Color::DarkGreen => FG_GREEN, + Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, + Color::DarkYellow => FG_GREEN | FG_RED, + Color::Blue => FG_INTENSITY | FG_BLUE, + Color::DarkBlue => FG_BLUE, + Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, + Color::DarkMagenta => FG_RED | FG_BLUE, + Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, + Color::DarkCyan => FG_GREEN | FG_BLUE, + Color::White => FG_RED | FG_GREEN | FG_BLUE, + Color::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, + + Color::Reset => { + // safe unwrap, initial console color was set with `init_console_color`. + let original_color = original_console_color(); + + const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; + // remove all background values from the original color, we don't want to reset those. + (original_color & !(REMOVE_BG_MASK)) + } + + /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ + Color::Rgb { r: _, g: _, b: _ } => 0, + Color::AnsiValue(_val) => 0, } - - /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, - }; - } - Colored::Bg(color) => { - winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => BG_INTENSITY, - Color::Red => BG_INTENSITY | BG_RED, - Color::DarkRed => BG_RED, - Color::Green => BG_INTENSITY | BG_GREEN, - Color::DarkGreen => BG_GREEN, - Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, - Color::DarkYellow => BG_GREEN | BG_RED, - Color::Blue => BG_INTENSITY | BG_BLUE, - Color::DarkBlue => BG_BLUE, - Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, - Color::DarkMagenta => BG_RED | BG_BLUE, - Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, - Color::DarkCyan => BG_GREEN | BG_BLUE, - Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, - Color::Grey => BG_RED | BG_GREEN | BG_BLUE, - - Color::Reset => { - let original_color = original_console_color(); - - const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; - // remove all foreground values from the original color, we don't want to reset those. - (original_color & !(REMOVE_FG_MASK)) + } + Colored::Bg(color) => { + match color { + Color::Black => 0, + Color::DarkGrey => BG_INTENSITY, + Color::Red => BG_INTENSITY | BG_RED, + Color::DarkRed => BG_RED, + Color::Green => BG_INTENSITY | BG_GREEN, + Color::DarkGreen => BG_GREEN, + Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, + Color::DarkYellow => BG_GREEN | BG_RED, + Color::Blue => BG_INTENSITY | BG_BLUE, + Color::DarkBlue => BG_BLUE, + Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, + Color::DarkMagenta => BG_RED | BG_BLUE, + Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, + Color::DarkCyan => BG_GREEN | BG_BLUE, + Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, + Color::Grey => BG_RED | BG_GREEN | BG_BLUE, + + Color::Reset => { + let original_color = original_console_color(); + + const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; + // remove all foreground values from the original color, we don't want to reset those. + (original_color & !(REMOVE_FG_MASK)) + } + /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ + Color::Rgb { r: _, g: _, b: _ } => 0, + Color::AnsiValue(_val) => 0, } - /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, - }; + } } - }; - - winapi_color + } } /// Initializes the default console color. It will will be skipped if it has already been initialized. @@ -182,7 +178,7 @@ fn init_console_color() -> Result<()> { /// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. fn original_console_color() -> u16 { - // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor` + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` ORIGINAL_CONSOLE_COLOR .lock() .unwrap() @@ -195,9 +191,7 @@ lazy_static! { #[cfg(test)] mod tests { - use crate::style::winapi::{ - color_value, WinApiColor, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, - }; + use crate::style::winapi::{WinApiColor, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED}; use crate::{Color, Colored}; use super::ORIGINAL_CONSOLE_COLOR; @@ -205,13 +199,13 @@ mod tests { #[test] fn test_parse_fg_color() { let colored = Colored::Fg(Color::Red); - assert_eq!(color_value(colored), FG_INTENSITY | FG_RED); + assert_eq!(Into::::into(colored), FG_INTENSITY | FG_RED); } #[test] fn test_parse_bg_color() { let colored = Colored::Bg(Color::Red); - assert_eq!(color_value(colored), BG_INTENSITY | BG_RED); + assert_eq!(Into::::into(colored), BG_INTENSITY | BG_RED); } #[test] From 033a9bf4e63d9c56d32c76eb39d2c823ab2a459c Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 15:07:41 +0200 Subject: [PATCH 11/21] CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e5d9d..0755e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Version master - Added unit tests - Restructured files +- Removed unsafe static code +- Improved documentation and added book page to lib.rs # Version 0.5.1 From 628f68d67f77a79ced71a2439068c78db9909574 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 15:44:18 +0200 Subject: [PATCH 12/21] round 2 --- CHANGELOG.md | 1 + src/lib.rs | 2 +- src/style/ansi.rs | 8 ++++---- src/style/winapi.rs | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0755e13..cfc5c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Version master + - Added unit tests - Restructured files - Removed unsafe static code diff --git a/src/lib.rs b/src/lib.rs index 8976c0c..8de61cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -376,7 +376,7 @@ impl Command for SetFg { type AnsiType = String; fn ansi_code(&self) -> Self::AnsiType { - ansi::set_fg_sequence(self.0) + ansi::set_fg_csi_sequence(self.0) } #[cfg(windows)] diff --git a/src/style/ansi.rs b/src/style/ansi.rs index c5184e5..cfce43c 100644 --- a/src/style/ansi.rs +++ b/src/style/ansi.rs @@ -5,7 +5,7 @@ use crossterm_utils::{csi, write_cout, Result}; use crate::{Attribute, Color, Colored, Style}; -pub(crate) fn set_fg_sequence(fg_color: Color) -> String { +pub(crate) fn set_fg_csi_sequence(fg_color: Color) -> String { format!(csi!("{}m"), Into::::into(Colored::Fg(fg_color))) } @@ -45,13 +45,13 @@ impl Style for AnsiColor { } } -impl Into for Colored { - fn into(self) -> String { +impl From for String { + fn from(colored: Colored) -> Self { let mut ansi_value = String::new(); let color; - match self { + match colored { Colored::Fg(new_color) => { if new_color == Color::Reset { ansi_value.push_str("39"); diff --git a/src/style/winapi.rs b/src/style/winapi.rs index 356af26..2e30e5b 100644 --- a/src/style/winapi.rs +++ b/src/style/winapi.rs @@ -91,10 +91,10 @@ impl Style for WinApiColor { } } -impl Into for Colored { +impl From for u16 { /// Returns the WinApi color value (u16) from the `Colored` struct. - fn into(self) -> u16 { - match self { + fn from(colored: Colored) -> Self { + match colored { Colored::Fg(color) => { match color { Color::Black => 0, From c6d7eea2d53403cc6a8fbf6e345dddebb6dc663c Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 15:05:10 +0200 Subject: [PATCH 13/21] set_fg => set_bg --- src/lib.rs | 7 ++++--- src/style/ansi.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8de61cf..bffb227 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,7 +187,7 @@ pub use crossterm_utils::{ execute, impl_display, queue, Command, ExecutableCommand, QueueableCommand, Result, }; -use style::ansi::{self, AnsiColor}; +use style::ansi::{self, }; #[cfg(windows)] use style::winapi::WinApiColor; use style::Style; @@ -324,7 +324,8 @@ impl TerminalColor { pub fn new() -> TerminalColor { #[cfg(windows)] let color = if supports_ansi() { - Box::from(AnsiColor::new()) as Box<(dyn Style + Sync + Send)> +// Box::from(AnsiColor::new()) as Box<(dyn Style + Sync + Send)> + WinApiColor::new() as Box<(dyn Style + Sync + Send)> } else { WinApiColor::new() as Box<(dyn Style + Sync + Send)> }; @@ -399,7 +400,7 @@ impl Command for SetBg { #[cfg(windows)] fn execute_winapi(&self) -> Result<()> { - WinApiColor::new().set_fg(self.0) + WinApiColor::new().set_bg(self.0) } } diff --git a/src/style/ansi.rs b/src/style/ansi.rs index cfce43c..406277e 100644 --- a/src/style/ansi.rs +++ b/src/style/ansi.rs @@ -30,7 +30,7 @@ impl AnsiColor { impl Style for AnsiColor { fn set_fg(&self, fg_color: Color) -> Result<()> { - write_cout!(set_fg_sequence(fg_color))?; + write_cout!(set_fg_csi_sequence(fg_color))?; Ok(()) } From 14bd87a63e39039eee7705e68b11280c39f07ebd Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 15:51:03 +0200 Subject: [PATCH 14/21] revert change --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bffb227..6b4b295 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,7 +187,7 @@ pub use crossterm_utils::{ execute, impl_display, queue, Command, ExecutableCommand, QueueableCommand, Result, }; -use style::ansi::{self, }; +use style::ansi::{self, AnsiColor}; #[cfg(windows)] use style::winapi::WinApiColor; use style::Style; @@ -324,8 +324,7 @@ impl TerminalColor { pub fn new() -> TerminalColor { #[cfg(windows)] let color = if supports_ansi() { -// Box::from(AnsiColor::new()) as Box<(dyn Style + Sync + Send)> - WinApiColor::new() as Box<(dyn Style + Sync + Send)> + Box::from(AnsiColor::new()) as Box<(dyn Style + Sync + Send)> } else { WinApiColor::new() as Box<(dyn Style + Sync + Send)> }; From 03e198ef60d11497368cb200139794772e05e001 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 21:54:12 +0200 Subject: [PATCH 15/21] reset color/set attr uses command api instead of stdout --- src/lib.rs | 19 ++++++++++++++++++- src/styledobject.rs | 9 ++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6b4b295..d43f8a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -439,11 +439,28 @@ where #[cfg(windows)] fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. Ok(()) } } +/// When executed, this command will reset the console colors back to default +/// +/// See `crossterm/examples/command.rs` for more information on how to execute commands. +pub struct ResetColor; + +impl Command for ResetColor { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::RESET_CSI_SEQUENCE.to_string() + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + WinApiColor::new().reset() + } +} + impl_display!(for SetFg); impl_display!(for SetBg); impl_display!(for SetAttr); diff --git a/src/styledobject.rs b/src/styledobject.rs index 7cdb2e8..2069402 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -3,9 +3,11 @@ use std::fmt::{self, Display, Formatter}; use std::result; -use crossterm_utils::{csi, queue}; +use crossterm_utils::queue; -use super::{color, Attribute, Color, Colorize, ObjectStyle, SetBg, SetFg, Styler}; +use crate::{ + color, Attribute, Color, Colorize, ObjectStyle, ResetColor, SetAttr, SetBg, SetFg, Styler, +}; /// Contains both the style and the content which can be styled. #[derive(Clone)] @@ -64,13 +66,14 @@ impl Display for StyledObject { } for attr in self.object_style.attrs.iter() { - fmt::Display::fmt(&format!(csi!("{}m"), *attr as i16), f)?; + queue!(f, SetAttr(*attr)).map_err(|_| fmt::Error)?; reset = true; } fmt::Display::fmt(&self.content, f)?; if reset { + queue!(f, ResetColor).map_err(|_| fmt::Error)?; colored_terminal.reset().map_err(|_| fmt::Error)?; } From 28e7d66b7dc1f246d266d31de5076f2580e2a751 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 21:54:30 +0200 Subject: [PATCH 16/21] remove terminal color instance --- src/styledobject.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/styledobject.rs b/src/styledobject.rs index 2069402..3452532 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -53,7 +53,6 @@ impl<'a, D: Display + 'a + Clone> StyledObject { impl Display for StyledObject { fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> { - let colored_terminal = color(); let mut reset = false; if let Some(bg) = self.object_style.bg_color { @@ -74,7 +73,6 @@ impl Display for StyledObject { if reset { queue!(f, ResetColor).map_err(|_| fmt::Error)?; - colored_terminal.reset().map_err(|_| fmt::Error)?; } Ok(()) From ac6afffe8e5721fe65124bb7ace3e5c63b506d12 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 21:58:53 +0200 Subject: [PATCH 17/21] updated change log --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc5c76..8c1f157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - Restructured files - Removed unsafe static code - Improved documentation and added book page to lib.rs +- Fixed bug with `SetBg` command, WinApi logic +- Fixed bug with `StyledObject`, used stdout for resetting terminal color +- Introduced `ResetColor` command # Version 0.5.1 From 029523f4ac247414199a39219aca945c0e491aae Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 22:00:51 +0200 Subject: [PATCH 18/21] impl display --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index d43f8a7..fc7dd99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -466,3 +466,4 @@ impl_display!(for SetBg); impl_display!(for SetAttr); impl_display!(for PrintStyledFont); impl_display!(for PrintStyledFont<&'static str>); +impl_display!(for ResetColor); \ No newline at end of file From 8bac9401815b4e5a998c89deea951a4f038188d9 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 22:06:47 +0200 Subject: [PATCH 19/21] comment wording --- src/lib.rs | 2 +- src/style/winapi.rs | 2 +- src/styledobject.rs | 9 +++------ src/traits.rs | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fc7dd99..7bed1bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -466,4 +466,4 @@ impl_display!(for SetBg); impl_display!(for SetAttr); impl_display!(for PrintStyledFont); impl_display!(for PrintStyledFont<&'static str>); -impl_display!(for ResetColor); \ No newline at end of file +impl_display!(for ResetColor); diff --git a/src/style/winapi.rs b/src/style/winapi.rs index 2e30e5b..b3583c8 100644 --- a/src/style/winapi.rs +++ b/src/style/winapi.rs @@ -4,9 +4,9 @@ use std::sync::Mutex; use crossterm_utils::Result; -use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; use winapi::um::wincon; +use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; use lazy_static::lazy_static; use crate::{Color, Colored, Style}; diff --git a/src/styledobject.rs b/src/styledobject.rs index 3452532..077cf9d 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -21,8 +21,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// /// # Remarks /// - /// This methods consumes 'self', and works like a builder. - /// You can do: `with().on().attr()` + /// This methods consumes 'self', and works like a builder, like: `with().on().attr()`. pub fn with(mut self, foreground_color: Color) -> StyledObject { self.object_style = self.object_style.fg(foreground_color); self @@ -32,8 +31,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// /// # Remarks /// - /// This methods consumes 'self', and works like a builder. - /// You can do: `with().on().attr()` + /// This methods consumes 'self', and works like a builder, like: `with().on().attr()`. pub fn on(mut self, background_color: Color) -> StyledObject { self.object_style = self.object_style.bg(background_color); self @@ -43,8 +41,7 @@ impl<'a, D: Display + 'a + Clone> StyledObject { /// /// # Remarks /// - /// This methods consumes 'self', and works like a builder. - /// You can do: `with().on().attr()` + /// This methods consumes 'self', and works like a builder, like: `with().on().attr()`. pub fn attr(mut self, attr: Attribute) -> StyledObject { self.object_style.add_attr(attr); self diff --git a/src/traits.rs b/src/traits.rs index 91d0978..9398355 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -4,7 +4,7 @@ use crate::StyledObject; /// Provides a set of methods to color any type implementing `Display` + `Clone`. /// -/// This trait has been implemented for `&static str` and `StyledObject`, you can invoke these methods there. +/// This trait is implemented for `&static str` and `StyledObject`, you can invoke these methods there. /// /// # Example /// @@ -52,7 +52,7 @@ pub trait Colorize { /// Provides a set of methods to style any type implementing `Display` with attributes. /// -/// This trait has been implemented for `&static str` and `StyledObject`, you can invoke these methods there. +/// This trait is implemented for `&static str` and `StyledObject`, you can invoke these methods there. /// /// # Example /// From 8d6211174b4065a9be018a76ea15e9d02a06536a Mon Sep 17 00:00:00 2001 From: Timon Post Date: Mon, 30 Sep 2019 09:45:58 +0200 Subject: [PATCH 20/21] removed import --- src/styledobject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styledobject.rs b/src/styledobject.rs index 077cf9d..2c0e1c1 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -6,7 +6,7 @@ use std::result; use crossterm_utils::queue; use crate::{ - color, Attribute, Color, Colorize, ObjectStyle, ResetColor, SetAttr, SetBg, SetFg, Styler, + Attribute, Color, Colorize, ObjectStyle, ResetColor, SetAttr, SetBg, SetFg, Styler, }; /// Contains both the style and the content which can be styled. From b5aecd46908d2df6331fc211191b00bb023fde96 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Mon, 30 Sep 2019 09:49:59 +0200 Subject: [PATCH 21/21] fmt --- src/styledobject.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/styledobject.rs b/src/styledobject.rs index 2c0e1c1..35bc001 100644 --- a/src/styledobject.rs +++ b/src/styledobject.rs @@ -5,9 +5,7 @@ use std::result; use crossterm_utils::queue; -use crate::{ - Attribute, Color, Colorize, ObjectStyle, ResetColor, SetAttr, SetBg, SetFg, Styler, -}; +use crate::{Attribute, Color, Colorize, ObjectStyle, ResetColor, SetAttr, SetBg, SetFg, Styler}; /// Contains both the style and the content which can be styled. #[derive(Clone)]