use crate::{Ansi, Toggle};
use crate::introspect::Attr;
use std::fmt;
#[derive(Eq, Clone, Copy, fmt::Debug)]
#[non_exhaustive]
pub enum Color {
Black,
Red,
Green,
Yellow,
Blue,
Purple,
Cyan,
White,
BrightBlack,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightPurple,
BrightCyan,
BrightWhite,
#[doc(hidden)]
#[cfg(feature="color256")]
ColorNum(u8),
#[doc(hidden)]
#[cfg(feature="rgb")]
Rgb(u8, u8, u8),
}
impl Color {
#[inline]
pub const fn reset() -> ColorReset { ColorReset }
#[cfg(any(feature="color256", doc))]
#[inline]
pub const fn num(i: u8) -> Color { Self::ColorNum(i) }
#[cfg(any(feature="rgb", doc))]
#[inline]
pub const fn rgb(r: u8, g: u8, b: u8) -> Color { Self::Rgb(r,g,b) }
#[inline]
pub const fn attr(&self) -> Attr<Color> { Attr::new(*self) }
#[inline]
pub const fn bg(&self) -> Ansi {
Ansi::from_color(*self, Toggle::Set, Coloree::Background)
}
#[inline]
pub const fn only(&self) -> Ansi { self.ansi().only() }
#[inline]
pub const fn important(&self) -> Ansi { self.ansi().important() }
#[inline]
pub const fn ansi(&self) -> Ansi {
Ansi::from_color(*self, Toggle::Set, Coloree::Text)
}
#[inline]
const fn _get_num_opt_unless_rgb(&self) -> Option<u8> {
match self {
Self::Black => Some( 0),
Self::Red => Some( 1),
Self::Green => Some( 2),
Self::Yellow => Some( 3),
Self::Blue => Some( 4),
Self::Purple => Some( 5),
Self::Cyan => Some( 6),
Self::White => Some( 7),
Self::BrightBlack => Some( 8),
Self::BrightRed => Some( 9),
Self::BrightGreen => Some(10),
Self::BrightYellow => Some(11),
Self::BrightBlue => Some(12),
Self::BrightPurple => Some(13),
Self::BrightCyan => Some(14),
Self::BrightWhite => Some(15),
#[cfg(feature="color256")]
Self::ColorNum(n) => Some(*n),
#[cfg(feature="rgb")]
Self::Rgb(_,_,_) => None,
}
}
#[cfg(any(not(feature="rgb"), doc))]
#[inline]
pub const fn get_num(&self) -> u8 {
self._get_num_opt_unless_rgb().unwrap()
}
#[cfg(any(feature="rgb", doc))]
#[inline]
pub const fn get_num_opt(&self) -> Option<u8> {
match self {
Self::Rgb(r, g, b) => Self::num_opt_from_rgb(*r, *g, *b),
_ => self._get_num_opt_unless_rgb(),
}
}
#[cfg(feature="rgb")]
const fn num_opt_from_rgb(r: u8, g: u8, b: u8) -> Option<u8> {
match (r, g, b) {
(192, 192, 192) => return Some(7),
(128, 128, 128) => return Some(8),
(0 | 128, 0 | 128, 0 | 128) => return Some(
0
+ if r == 0 { 0 } else { 1 }
+ if g == 0 { 0 } else { 2 }
+ if b == 0 { 0 } else { 4 }
),
(0 | 255, 0 | 255, 0 | 255) => return Some(
8
+ if r == 0 { 0 } else { 1 }
+ if g == 0 { 0 } else { 2 }
+ if b == 0 { 0 } else { 4 }
),
_ => ()
}
match (r, g, b) {
(0 | 95 | 135 | 175 | 215 | 255,
0 | 95 | 135 | 175 | 215 | 255,
0 | 95 | 135 | 175 | 215 | 255) => return Some(
16
+ if r == 0 { 0 } else { ((r - 95) / 40 + 1) * 36 }
+ if g == 0 { 0 } else { ((g - 95) / 40 + 1) * 6 }
+ if b == 0 { 0 } else { ((b - 95) / 40 + 1) * 1 }
),
_ => (),
}
if r == g
&& g == b
&& r >= 8
&& (r - 8) % 10 == 0 {
let num = (r - 8) / 10;
return match num {
12 => Some(8),
24 => None,
_ => Some(num + 232),
};
}
None
}
const fn rgb_from_num(index: u8) -> (u8,u8,u8) {
match index {
0..=15 => { let level = match index {
9.. => 255,
7 => 192,
_ => 128,
};
let r = if index == 8 { 128 }
else if (index & 1) != 0 { level }
else { 0 };
let g = if index == 8 { 128 }
else if (index & 2) != 0 { level }
else { 0 };
let b = if index == 8 { 128 }
else if (index & 4) != 0 { level }
else { 0 };
(r,g,b)
},
16..=231 => { let index = index - 16;
let r_index = (index / 36) % 6;
let g_index = (index / 6) % 6;
let b_index = index % 6;
let r = if r_index == 0 { 0 } else { 95 + 40 * (r_index-1) };
let g = if g_index == 0 { 0 } else { 95 + 40 * (g_index-1) };
let b = if b_index == 0 { 0 } else { 95 + 40 * (b_index-1) };
(r,g,b)
}
_ => { let index = index - 232;
let level = index * 10 + 8;
(level, level, level)
}
}
}
#[cfg(any(not(feature="rgb"), doc))]
#[inline]
pub const fn get_rgb(&self) -> (u8,u8,u8) {
Self::rgb_from_num(self.get_num())
}
#[doc(hidden)]
#[cfg(feature="rgb")]
#[inline]
pub const fn get_rgb(&self) -> (u8,u8,u8) {
match self {
Self::Rgb(r,g,b) => (*r,*g,*b),
_ => {
let num = self._get_num_opt_unless_rgb().unwrap();
Self::rgb_from_num(num)
},
}
}
}
#[cfg(not(feature="rgb"))]
impl PartialEq for Color {
fn eq(&self, other: &Self) -> bool {
self.get_num() == other.get_num()
}
}
#[cfg(feature="rgb")]
impl PartialEq for Color {
fn eq(&self, other: &Self) -> bool {
match (self._get_num_opt_unless_rgb(), other._get_num_opt_unless_rgb()) {
(Some(a), Some(b)) => a == b,
_ => {
let a = self.get_rgb();
let b = other.get_rgb();
a.0 == b.0 && a.1 == b.1 && a.2 == b.2
},
}
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.ansi(), f) }
}
pub struct ColorReset;
impl ColorReset {
#[inline]
pub const fn bg(&self) -> Ansi {
Ansi::from_color(Color::Black, Toggle::Reset, Coloree::Background)
}
#[inline]
pub const fn only(&self) -> Ansi { self.ansi().only() }
#[inline]
pub const fn important(&self) -> Ansi { self.ansi().important() }
#[inline]
pub const fn ansi(&self) -> Ansi {
Ansi::from_color(Color::Black, Toggle::Reset, Coloree::Text)
}
}
impl fmt::Display for ColorReset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.ansi(), f) }
}
#[derive(PartialEq, Eq, Clone, Copy, fmt::Debug)]
#[non_exhaustive]
pub enum Coloree {
Text,
Background,
}
impl Coloree {
const VARIANTS: &'static[Coloree] = &[Self::Text, Self::Background];
pub const fn all() -> &'static[Coloree] { Self::VARIANTS }
}