use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
r: u8,
g: u8,
b: u8,
}
impl Color {
pub fn new(r: u8, g: u8, b: u8) -> Self {
Color { r, g, b }
}
pub fn to_rgb(&self) -> (u8, u8, u8) {
(self.r, self.g, self.b)
}
pub fn as_fg(&self) -> String {
format!("\x1b[38;2;{};{};{}m", self.r, self.g, self.b)
}
pub fn as_bg(&self) -> String {
format!("\x1b[48;2;{};{};{}m", self.r, self.g, self.b)
}
}
impl From<(u8, u8, u8)> for Color {
fn from(rgb: (u8, u8, u8)) -> Self {
Color::new(rgb.0, rgb.1, rgb.2)
}
}
macro_rules! define_colors {
($($name:ident => ($r:expr, $g:expr, $b:expr)),* $(,)?) => {
$(pub const $name: Color = Color { r: $r, g: $g, b: $b };)*
};
}
define_colors!(
BLACK => (0, 0, 0),
BLUE => (0, 0, 255),
GREEN => (0, 255, 0),
CYAN => (0, 255, 255),
RED => (255, 0, 0),
MAGENTA => (255, 0, 255),
YELLOW => (255, 255, 0),
WHITE => (255, 255, 255)
);
macro_rules! create_style_enum {
($(($style:ident, $code:expr)),* $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Style {$($style,)*}
impl Style {
pub fn code(&self) -> String {
match self {$(Style::$style => format!("\x1b[{}m", $code),)*}
}
}
};
}
create_style_enum! {
(Bold, 1), (Dim, 2), (Italic, 3), (Underline, 4), (Hidden, 8), }
pub trait Stylize {
fn color(&self, color: Color) -> String;
fn on_color(&self, color: Color) -> String;
fn style(&self, style: Style) -> String;
}
macro_rules! impl_stylize {
($($t:ty)*) => ($(
impl Stylize for $t {
fn color(&self, color: Color) -> String {format!("{}{}\x1b[0m", color.as_fg(), self)}
fn on_color(&self, color: Color) -> String {format!("{}{}\x1b[0m", color.as_bg(), self)}
fn style(&self, style: Style) -> String {format!("{}{}\x1b[0m", style.code(), self)}
}
)*)
}
impl_stylize! { str String }
pub fn strip_ansi_codes(s: &str) -> String {
#[derive(Clone, Copy)]
enum State {
Normal,
Escape,
Csi,
}
s.chars()
.scan(State::Normal, |state, c| match (*state, c) {
(State::Normal, '\x1B') => {
*state = State::Escape;
Some(None)
}
(State::Escape, '[') => {
*state = State::Csi;
Some(None)
}
(State::Escape, _) => {
*state = State::Normal;
Some(Some('\x1B'))
}
(State::Csi, 'm') => {
*state = State::Normal;
Some(None)
}
(State::Csi, '0'..='9') | (State::Csi, ';') => Some(None),
(State::Csi, _) => {
*state = State::Normal;
Some(Some(c))
}
(State::Normal, c) => Some(Some(c)),
})
.flatten()
.collect()
}
pub fn visual_length(s: &str) -> usize {
strip_ansi_codes(s).chars().count()
}