API Cleanup - part 2 (#238)

This commit is contained in:
Zrzka 2019-09-19 17:39:14 +02:00 committed by Timon
parent 06c791714a
commit 4952cb33d9
25 changed files with 138 additions and 143 deletions

View File

@ -8,23 +8,22 @@ use crate::sys::{get_cursor_position, show_cursor};
use super::ITerminalCursor; use super::ITerminalCursor;
#[inline]
pub fn get_goto_ansi(x: u16, y: u16) -> String { pub fn get_goto_ansi(x: u16, y: u16) -> String {
format!(csi!("{};{}H"), y + 1, x + 1) format!(csi!("{};{}H"), y + 1, x + 1)
} }
#[inline]
pub fn get_move_up_ansi(count: u16) -> String { pub fn get_move_up_ansi(count: u16) -> String {
format!(csi!("{}A"), count) format!(csi!("{}A"), count)
} }
#[inline]
pub fn get_move_right_ansi(count: u16) -> String { pub fn get_move_right_ansi(count: u16) -> String {
format!(csi!("{}C"), count) format!(csi!("{}C"), count)
} }
#[inline]
pub fn get_move_down_ansi(count: u16) -> String { pub fn get_move_down_ansi(count: u16) -> String {
format!(csi!("{}B"), count) format!(csi!("{}B"), count)
} }
#[inline]
pub fn get_move_left_ansi(count: u16) -> String { pub fn get_move_left_ansi(count: u16) -> String {
format!(csi!("{}D"), count) format!(csi!("{}D"), count)
} }

View File

@ -83,7 +83,7 @@ impl TerminalCursor {
/// Move the current cursor position `n` times left. /// Move the current cursor position `n` times left.
pub fn move_left(&mut self, count: u16) -> Result<&mut TerminalCursor> { pub fn move_left(&mut self, count: u16) -> Result<&mut TerminalCursor> {
self.cursor.move_left(count).unwrap(); self.cursor.move_left(count)?;
Ok(self) Ok(self)
} }

View File

@ -129,7 +129,7 @@ impl AsyncReader {
/// # Remarks /// # Remarks
/// - Background thread will be closed. /// - Background thread will be closed.
/// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead. /// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead.
pub fn stop_reading(&mut self) { pub fn stop(&mut self) {
self.shutdown.store(true, Ordering::SeqCst); self.shutdown.store(true, Ordering::SeqCst);
} }
} }
@ -161,7 +161,7 @@ impl Iterator for AsyncReader {
impl Drop for AsyncReader { impl Drop for AsyncReader {
fn drop(&mut self) { fn drop(&mut self) {
self.stop_reading(); self.stop();
} }
} }
@ -490,13 +490,17 @@ where
} }
#[cfg(test)] #[cfg(test)]
#[test] mod tests {
fn test_parse_utf8() { use super::parse_utf8_char;
let st = "abcéŷ¤£€ù%323";
let ref mut bytes = st.bytes(); #[test]
let chars = st.chars(); fn test_parse_utf8() {
for c in chars { let st = "abcéŷ¤£€ù%323";
let b = bytes.next().unwrap(); let ref mut bytes = st.bytes();
assert_eq!(c, parse_utf8_char(b, bytes).unwrap()); let chars = st.chars();
for c in chars {
let b = bytes.next().unwrap();
assert_eq!(c, parse_utf8_char(b, bytes).unwrap());
}
} }
} }

View File

@ -180,14 +180,14 @@ impl AsyncReader {
/// # Remarks /// # Remarks
/// - Background thread will be closed. /// - Background thread will be closed.
/// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead. /// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead.
pub fn stop_reading(&mut self) { pub fn stop(&mut self) {
self.shutdown.store(true, Ordering::SeqCst); self.shutdown.store(true, Ordering::SeqCst);
} }
} }
impl Drop for AsyncReader { impl Drop for AsyncReader {
fn drop(&mut self) { fn drop(&mut self) {
self.stop_reading(); self.stop();
} }
} }

View File

@ -53,8 +53,8 @@ impl RawScreen {
Ok(()) Ok(())
} }
/// This will disable the drop logic of this type, which means that the rawscreen will not be disabled when this instance goes out of scope. /// Keeps the raw mode when the `RawMode` value is dropped.
pub fn disable_raw_mode_on_drop(&mut self) { pub fn keep_raw_mode_on_drop(&mut self) {
self.disable_raw_mode_on_drop = false; self.disable_raw_mode_on_drop = false;
} }
} }

View File

@ -18,7 +18,7 @@ impl ToAlternateScreenCommand {
impl IAlternateScreenCommand for ToAlternateScreenCommand { impl IAlternateScreenCommand for ToAlternateScreenCommand {
/// enable alternate screen. /// enable alternate screen.
fn enable(&self) -> Result<()> { fn enable(&self) -> Result<()> {
write_cout!(csi!("?1049h")).unwrap(); write_cout!(csi!("?1049h"))?;
Ok(()) Ok(())
} }

View File

@ -5,17 +5,14 @@ use crossterm_utils::{csi, write_cout, Result};
use crate::{Attribute, Color, Colored, ITerminalColor}; use crate::{Attribute, Color, Colored, ITerminalColor};
#[inline]
pub fn get_set_fg_ansi(fg_color: Color) -> String { pub fn get_set_fg_ansi(fg_color: Color) -> String {
format!(csi!("{}m"), color_value(Colored::Fg(fg_color)),) format!(csi!("{}m"), color_value(Colored::Fg(fg_color)),)
} }
#[inline]
pub fn get_set_bg_ansi(bg_color: Color) -> String { pub fn get_set_bg_ansi(bg_color: Color) -> String {
format!(csi!("{}m"), color_value(Colored::Bg(bg_color)),) format!(csi!("{}m"), color_value(Colored::Bg(bg_color)),)
} }
#[inline]
pub fn get_set_attr_ansi(attribute: Attribute) -> String { pub fn get_set_attr_ansi(attribute: Attribute) -> String {
format!(csi!("{}m"), attribute as i16,) format!(csi!("{}m"), attribute as i16,)
} }

View File

@ -2,6 +2,7 @@
//! Like applying attributes to text and changing the foreground and background. //! Like applying attributes to text and changing the foreground and background.
use std::clone::Clone; use std::clone::Clone;
use std::env;
use std::fmt::Display; use std::fmt::Display;
#[cfg(windows)] #[cfg(windows)]
@ -65,19 +66,14 @@ impl TerminalColor {
} }
/// Get available color count. /// Get available color count.
/// (This does not always provide a good result.) ///
pub fn get_available_color_count(&self) -> Result<u16> { /// # Remarks
use std::env; ///
Ok(match env::var_os("TERM") { /// This does not always provide a good result.
Some(val) => { pub fn get_available_color_count(&self) -> u16 {
if val.to_str().unwrap_or("").contains("256color") { env::var("TERM")
256 .map(|x| if x.contains("256color") { 256 } else { 8 })
} else { .unwrap_or(8)
8
}
}
None => 8,
})
} }
} }

View File

@ -46,24 +46,15 @@ pub enum Color {
AnsiValue(u8), AnsiValue(u8),
} }
impl<'a> From<&'a str> for Color {
/// Get a `Color` from a `&str` like `Color::from("blue")`.
fn from(src: &str) -> Self {
src.parse().unwrap_or(Color::White)
}
}
impl From<String> for Color {
/// Get a `Color` from a `&str` like `Color::from(String::from(blue))`.
fn from(src: String) -> Self {
src.parse().unwrap_or(Color::White)
}
}
impl FromStr for Color { impl FromStr for Color {
type Err = (); type Err = ();
/// Convert a `&str` to a `Color` value /// 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<Self, Self::Err> { fn from_str(src: &str) -> ::std::result::Result<Self, Self::Err> {
let src = src.to_lowercase(); let src = src.to_lowercase();
@ -88,3 +79,33 @@ impl FromStr for Color {
} }
} }
} }
#[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));
}
}

View File

@ -37,12 +37,12 @@ impl Display for Colored {
let colored_terminal = color(); let colored_terminal = color();
match *self { match *self {
Colored::Fg(color) => { Colored::Fg(color) => colored_terminal
colored_terminal.set_fg(color).unwrap(); .set_fg(color)
} .map_err(|_| std::fmt::Error)?,
Colored::Bg(color) => { Colored::Bg(color) => colored_terminal
colored_terminal.set_bg(color).unwrap(); .set_bg(color)
} .map_err(|_| std::fmt::Error)?,
} }
Ok(()) Ok(())

View File

@ -1,34 +1,32 @@
macro_rules! def_attr { macro_rules! def_attr {
($name: ident => $attr: path) => { ($name:ident => $attr:path) => {
fn $name(self) -> StyledObject<D> { fn $name(self) -> StyledObject<D> {
let so = self; self.attr($attr)
so.attr($attr)
} }
}; };
} }
macro_rules! def_color { macro_rules! def_color {
($side:ident: $name: ident => $color: path) => { ($side:ident: $name:ident => $color:path) => {
fn $name(self) -> StyledObject<D> { fn $name(self) -> StyledObject<D> {
StyledObject { StyledObject {
object_style: ObjectStyle { object_style: ObjectStyle {
$side: Some($color), $side: Some($color),
.. self.object_style ..self.object_style
}, },
.. self ..self
} }
} }
}; };
} }
macro_rules! def_str_color { macro_rules! def_str_color {
($side:ident: $name: ident => $color: path) => { ($side:ident: $name:ident => $color:path) => {
fn $name(self) -> StyledObject< &'static str> { fn $name(self) -> StyledObject< &'static str> {
StyledObject { StyledObject {
object_style: ObjectStyle { object_style: ObjectStyle {
$side: Some($color), $side: Some($color),
.. ObjectStyle::default() ..Default::default()
}, },
content: self content: self
} }
@ -37,12 +35,12 @@ macro_rules! def_str_color {
} }
macro_rules! def_str_attr { macro_rules! def_str_attr {
($name: ident => $color: path) => { ($name:ident => $color:path) => {
fn $name(self) -> StyledObject<&'static str> { fn $name(self) -> StyledObject<&'static str> {
StyledObject { StyledObject {
object_style: ObjectStyle::default(), object_style: Default::default(),
content: self content: self,
} }
} }
} }
} }

View File

@ -5,23 +5,13 @@ use std::fmt::Display;
use super::{Attribute, Color, StyledObject}; use super::{Attribute, Color, StyledObject};
/// Struct that contains the style properties that can be applied to a displayable object. /// Struct that contains the style properties that can be applied to a displayable object.
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct ObjectStyle { pub struct ObjectStyle {
pub fg_color: Option<Color>, pub fg_color: Option<Color>,
pub bg_color: Option<Color>, pub bg_color: Option<Color>,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
} }
impl Default for ObjectStyle {
fn default() -> ObjectStyle {
ObjectStyle {
fg_color: None,
bg_color: None,
attrs: Vec::new(),
}
}
}
impl ObjectStyle { impl ObjectStyle {
/// Apply a `StyledObject` to the passed displayable object. /// Apply a `StyledObject` to the passed displayable object.
pub fn apply_to<D: Display + Clone>(&self, val: D) -> StyledObject<D> { pub fn apply_to<D: Display + Clone>(&self, val: D) -> StyledObject<D> {
@ -33,11 +23,7 @@ impl ObjectStyle {
/// Get a new instance of `ObjectStyle` /// Get a new instance of `ObjectStyle`
pub fn new() -> ObjectStyle { pub fn new() -> ObjectStyle {
ObjectStyle { ObjectStyle::default()
fg_color: None,
bg_color: None,
attrs: Vec::new(),
}
} }
/// Set the background color of `ObjectStyle` to the passed color. /// Set the background color of `ObjectStyle` to the passed color.

View File

@ -55,11 +55,11 @@ impl<D: Display + Clone> Display for StyledObject<D> {
let mut reset = false; let mut reset = false;
if let Some(bg) = self.object_style.bg_color { if let Some(bg) = self.object_style.bg_color {
queue!(f, SetBg(bg)).unwrap(); queue!(f, SetBg(bg)).map_err(|_| fmt::Error)?;
reset = true; reset = true;
} }
if let Some(fg) = self.object_style.fg_color { if let Some(fg) = self.object_style.fg_color {
queue!(f, SetFg(fg)).unwrap(); queue!(f, SetFg(fg)).map_err(|_| fmt::Error)?;
reset = true; reset = true;
} }
@ -71,7 +71,7 @@ impl<D: Display + Clone> Display for StyledObject<D> {
fmt::Display::fmt(&self.content, f)?; fmt::Display::fmt(&self.content, f)?;
if reset { if reset {
colored_terminal.reset().unwrap(); colored_terminal.reset().map_err(|_| fmt::Error)?;
} }
Ok(()) Ok(())

View File

@ -6,8 +6,8 @@ use crate::StyledObject;
/// ///
/// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them. /// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them.
/// ///
/// ```ignore /// ```rust
/// use Colorizer; /// use crossterm_style::Colorize;
/// ///
/// let styled_text = "Red forground color on blue background.".red().on_blue(); /// let styled_text = "Red forground color on blue background.".red().on_blue();
/// println!("{}", styled_text); /// println!("{}", styled_text);
@ -53,12 +53,13 @@ pub trait Colorize<D: 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 is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them.
/// ///
/// # Example /// # Example
/// ```ignore
/// use Colorizer;
/// ///
/// println!("{}", "Bold text".bold(); /// ```rust
/// println!("{}", "Underlined text".underlined(); /// use crossterm_style::Styler;
/// println!("{}", "Negative text".negative(); ///
/// println!("{}", "Bold text".bold());
/// println!("{}", "Underlined text".underlined());
/// println!("{}", "Negative text".negative());
/// ``` /// ```
pub trait Styler<D: Display + Clone> { pub trait Styler<D: Display + Clone> {
fn reset(self) -> StyledObject<D>; fn reset(self) -> StyledObject<D>;

View File

@ -44,7 +44,7 @@ impl ITerminalColor for WinApiColor {
let mut color: u16; let mut color: u16;
let attrs = csbi.attributes(); let attrs = csbi.attributes();
let bg_color = attrs & 0x0070; let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | bg_color; color = color_value.parse::<u16>()? | bg_color;
// background intensity is a separate value in attrs, // background intensity is a separate value in attrs,
// wee need to check if this was applied to the current bg color. // wee need to check if this was applied to the current bg color.
@ -71,7 +71,7 @@ impl ITerminalColor for WinApiColor {
let mut color: u16; let mut color: u16;
let attrs = csbi.attributes(); let attrs = csbi.attributes();
let fg_color = attrs & 0x0007; let fg_color = attrs & 0x0007;
color = fg_color | color_value.parse::<u16>().unwrap(); color = fg_color | color_value.parse::<u16>()?;
// Foreground intensity is a separate value in attrs, // Foreground intensity is a separate value in attrs,
// So we need to check if this was applied to the current fg color. // So we need to check if this was applied to the current fg color.

View File

@ -14,17 +14,14 @@ pub static CLEAR_FROM_CURSOR_UP: &'static str = csi!("1J");
pub static CLEAR_FROM_CURRENT_LINE: &'static str = csi!("2K"); pub static CLEAR_FROM_CURRENT_LINE: &'static str = csi!("2K");
pub static CLEAR_UNTIL_NEW_LINE: &'static str = csi!("K"); pub static CLEAR_UNTIL_NEW_LINE: &'static str = csi!("K");
#[inline]
pub fn get_scroll_up_ansi(count: u16) -> String { pub fn get_scroll_up_ansi(count: u16) -> String {
format!(csi!("{}S"), count) format!(csi!("{}S"), count)
} }
#[inline]
pub fn get_scroll_down_ansi(count: u16) -> String { pub fn get_scroll_down_ansi(count: u16) -> String {
format!(csi!("{}T"), count) format!(csi!("{}T"), count)
} }
#[inline]
pub fn get_set_size_ansi(width: u16, height: u16) -> String { pub fn get_set_size_ansi(width: u16, height: u16) -> String {
format!(csi!("8;{};{}t"), height, width) format!(csi!("8;{};{}t"), height, width)
} }

View File

@ -5,6 +5,8 @@ use std::{
io, io,
}; };
use crate::impl_from;
/// The `crossterm` result type. /// The `crossterm` result type.
pub type Result<T> = std::result::Result<T, ErrorKind>; pub type Result<T> = std::result::Result<T, ErrorKind>;
@ -23,8 +25,11 @@ pub enum ErrorKind {
impl std::error::Error for ErrorKind { impl std::error::Error for ErrorKind {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self { match self {
ErrorKind::IoError(ref e) => Some(e), ErrorKind::IoError(e) => Some(e),
ErrorKind::FmtError(e) => Some(e),
ErrorKind::Utf8Error(e) => Some(e),
ErrorKind::ParseIntError(e) => Some(e),
_ => None, _ => None,
} }
} }
@ -40,26 +45,7 @@ impl Display for ErrorKind {
} }
} }
impl From<io::Error> for ErrorKind { impl_from!(io::Error, ErrorKind::IoError);
fn from(e: io::Error) -> ErrorKind { impl_from!(fmt::Error, ErrorKind::FmtError);
ErrorKind::IoError(e) impl_from!(std::string::FromUtf8Error, ErrorKind::Utf8Error);
} impl_from!(std::num::ParseIntError, ErrorKind::ParseIntError);
}
impl From<fmt::Error> for ErrorKind {
fn from(e: fmt::Error) -> ErrorKind {
ErrorKind::FmtError(e)
}
}
impl From<std::string::FromUtf8Error> for ErrorKind {
fn from(e: std::string::FromUtf8Error) -> Self {
ErrorKind::Utf8Error(e)
}
}
impl From<std::num::ParseIntError> for ErrorKind {
fn from(e: std::num::ParseIntError) -> Self {
ErrorKind::ParseIntError(e)
}
}

View File

@ -190,3 +190,14 @@ macro_rules! impl_display {
})* })*
} }
} }
#[macro_export]
macro_rules! impl_from {
($from:path, $to:expr) => {
impl From<$from> for ErrorKind {
fn from(e: $from) -> Self {
$to(e)
}
}
};
}

View File

@ -9,7 +9,7 @@ use crate::{ErrorKind, Result};
static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None; static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None;
pub static mut RAW_MODE_ENABLED: bool = false; pub static mut RAW_MODE_ENABLED: bool = false;
fn unwrap(t: i32) -> Result<()> { fn wrap_with_result(t: i32) -> Result<()> {
if t == -1 { if t == -1 {
Err(ErrorKind::IoError(io::Error::last_os_error())) Err(ErrorKind::IoError(io::Error::last_os_error()))
} else { } else {
@ -31,7 +31,7 @@ pub fn get_terminal_attr() -> Result<Termios> {
} }
unsafe { unsafe {
let mut termios = mem::zeroed(); let mut termios = mem::zeroed();
unwrap(tcgetattr(0, &mut termios))?; wrap_with_result(tcgetattr(0, &mut termios))?;
Ok(termios) Ok(termios)
} }
} }
@ -40,7 +40,7 @@ pub fn set_terminal_attr(termios: &Termios) -> Result<()> {
extern "C" { extern "C" {
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int; pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
} }
unwrap(unsafe { tcsetattr(0, 0, termios) }).and(Ok(())) wrap_with_result(unsafe { tcsetattr(0, 0, termios) })
} }
pub fn into_raw_mode() -> Result<()> { pub fn into_raw_mode() -> Result<()> {
@ -61,9 +61,8 @@ pub fn into_raw_mode() -> Result<()> {
pub fn disable_raw_mode() -> Result<()> { pub fn disable_raw_mode() -> Result<()> {
unsafe { unsafe {
if ORIGINAL_TERMINAL_MODE.is_some() { if let Some(original_terminal_mode) = ORIGINAL_TERMINAL_MODE.as_ref() {
set_terminal_attr(&ORIGINAL_TERMINAL_MODE.unwrap())?; set_terminal_attr(original_terminal_mode)?;
RAW_MODE_ENABLED = false; RAW_MODE_ENABLED = false;
} }
} }

View File

@ -24,11 +24,11 @@ pub mod ansi {
} else { } else {
old_mode & !mask old_mode & !mask
}; };
if old_mode == new_mode {
return Ok(()); if old_mode != new_mode {
console_mode.set_mode(new_mode)?;
} }
console_mode.set_mode(new_mode)?;
Ok(()) Ok(())
} }
} }

View File

@ -76,7 +76,7 @@ impl From<Handle> for ConsoleMode {
} }
#[cfg(test)] #[cfg(test)]
mod test { mod tests {
use super::ConsoleMode; use super::ConsoleMode;
// TODO - Test is ignored, because it's failing on Travis CI // TODO - Test is ignored, because it's failing on Travis CI

View File

@ -175,7 +175,7 @@ impl From<HANDLE> for Handle {
} }
#[cfg(test)] #[cfg(test)]
mod test { mod tests {
use super::{Handle, HandleType}; use super::{Handle, HandleType};
#[test] #[test]

View File

@ -122,7 +122,7 @@ impl From<HANDLE> for ScreenBuffer {
} }
#[cfg(test)] #[cfg(test)]
mod test { mod tests {
use super::ScreenBuffer; use super::ScreenBuffer;
#[test] #[test]

View File

@ -20,7 +20,7 @@ fn main() -> Result<()> {
/// run the program /// run the program
fn run() -> Result<()> { fn run() -> Result<()> {
// let screen = RawScreen::into_raw_mode().expect("failed to enable raw modes"); // let screen = RawScreen::into_raw_mode()?;
print_welcome_screen()?; print_welcome_screen()?;
start_algorithm()?; start_algorithm()?;
exit() exit()
@ -90,7 +90,7 @@ fn print_welcome_screen() -> Result<()> {
} }
fn exit() -> Result<()> { fn exit() -> Result<()> {
RawScreen::disable_raw_mode().expect("Failed to disable raw modes."); RawScreen::disable_raw_mode()?;
cursor().show()?; cursor().show()?;
color().reset() color().reset()
} }

View File

@ -399,7 +399,7 @@ fn print_text_with_attributes() {
/// Print all supported RGB colors, not supported for Windows systems < 10 | demonstration. /// Print all supported RGB colors, not supported for Windows systems < 10 | demonstration.
fn print_supported_colors() { fn print_supported_colors() {
let count = color().get_available_color_count().unwrap(); let count = color().get_available_color_count();
for i in 0..count { for i in 0..count {
println!("Test {}", Colored::Bg(Color::AnsiValue(i as u8))); println!("Test {}", Colored::Bg(Color::AnsiValue(i as u8)));