crab-vault-utils 0.2.11

All the utils of crab vault
Documentation
use std::fmt::Display;

use crate::bitmap::Bitmap;

pub const RESET: &str = "\x1B[0m";
pub const ESCAPE_BEGIN: &str = "\x1B[";
pub const ESCAPE_OVER: &str = "m";

pub const BOLD: usize = 1;
pub const DIMMED: usize = 2;
pub const ITALIC: usize = 3;
pub const UNDERLINE: usize = 4;
pub const BLINK_SLOWLY: usize = 5;
pub const BLINK_RAPIDLY: usize = 6;
pub const REVERSE: usize = 7;
pub const HIDDEN: usize = 8;
pub const STRIKE_THROUGH: usize = 9;

#[derive(Clone, Copy, Default)]
pub struct FontStyle {
    options: Bitmap<u16>,
}

#[derive(Clone, Copy)]
pub enum AnsiColor {
    Black = 30,
    Red,
    Green,
    Yellow,
    Blue,
    Magenta,
    Cyan,
    White,
    BrightBlack = 90,
    BrightRed,
    BrightGreen,
    BrightYellow,
    BrightBlue,
    BrightMagenta,
    BrightCyan,
    BrightWhite,
}

#[derive(Clone, Copy, Default)]
pub struct AnsiStyle {
    fore: Option<AnsiColor>,
    back: Option<AnsiColor>,
    font_style: FontStyle,
}

#[derive(Clone, Copy)]
pub struct AnsiString<'a> {
    style: AnsiStyle,
    is_vanilla: bool,
    content: &'a str,
}

impl AnsiColor {
    #[inline(always)]
    pub fn into_fore(self) -> u8 {
        self as u8
    }

    #[inline(always)]
    pub fn into_back(self) -> u8 {
        self as u8 + 10
    }
}

impl<'a> Display for AnsiString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.is_vanilla {
            f.pad(self.content)
        } else {
            f.write_fmt(format_args!("{}", self.style))?;
            f.pad(self.content)?;
            f.write_str(RESET)
        }
    }
}

impl FontStyle {
    pub fn new() -> Self {
        Self {
            options: Bitmap::new(),
        }
    }

    pub fn merge(self, rhs: FontStyle) -> FontStyle {
        Self {
            options: self.options | rhs.options,
        }
    }

    pub fn enabled(self, idx: usize) -> bool {
        self.options.get(idx)
    }
}

impl FontStyle {
    pub fn bold(mut self, enabled: bool) -> Self {
        self.options.set(BOLD, enabled);
        self
    }

    pub fn dimmed(mut self, enabled: bool) -> Self {
        self.options.set(DIMMED, enabled);
        self
    }

    pub fn italic(mut self, enabled: bool) -> Self {
        self.options.set(ITALIC, enabled);
        self
    }

    pub fn underline(mut self, enabled: bool) -> Self {
        self.options.set(UNDERLINE, enabled);
        self
    }

    pub fn blink_slowly(mut self, enabled: bool) -> Self {
        self.options.set(BLINK_SLOWLY, enabled);
        self
    }

    pub fn blink_rapidly(mut self, enabled: bool) -> Self {
        self.options.set(BLINK_RAPIDLY, enabled);
        self
    }

    pub fn reverse(mut self, enabled: bool) -> Self {
        self.options.set(REVERSE, enabled);
        self
    }

    pub fn hidden(mut self, enabled: bool) -> Self {
        self.options.set(HIDDEN, enabled);
        self
    }

    pub fn strike_through(mut self, enabled: bool) -> Self {
        self.options.set(STRIKE_THROUGH, enabled);
        self
    }
}

impl AnsiStyle {
    pub fn new() -> Self {
        Self {
            fore: None,
            back: None,
            font_style: FontStyle::new(),
        }
    }

    #[inline(always)]
    pub fn new_vanilla() -> Self {
        Self::new()
    }

    pub fn merge_style(mut self, other: FontStyle) -> Self {
        self.font_style = self.font_style.merge(other);
        self
    }

    pub fn with_fore(mut self, color: AnsiColor) -> Self {
        self.fore = Some(color);
        self
    }

    pub fn with_back(mut self, color: AnsiColor) -> Self {
        self.back = Some(color);
        self
    }

    pub fn is_vanilla(self) -> bool {
        self.fore.is_none() && self.back.is_none()
    }

    pub fn decorate<'a>(self, content: &'a str) -> AnsiString<'a> {
        AnsiString {
            style: self,
            is_vanilla: self.is_vanilla(),
            content,
        }
    }
}

impl AnsiStyle {
    pub fn bold(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.bold(enabled);
        self
    }

    pub fn dimmed(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.dimmed(enabled);
        self
    }

    pub fn italic(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.italic(enabled);
        self
    }

    pub fn underline(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.underline(enabled);
        self
    }

    pub fn blink_slowly(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.blink_slowly(enabled);
        self
    }

    pub fn blink_rapidly(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.blink_rapidly(enabled);
        self
    }

    pub fn reverse(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.reverse(enabled);
        self
    }

    pub fn hidden(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.hidden(enabled);
        self
    }

    pub fn strike_through(mut self, enabled: bool) -> Self {
        self.font_style = self.font_style.strike_through(enabled);
        self
    }
}

impl Display for AnsiStyle {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.is_vanilla() {
            Ok(())
        } else {
            f.write_str(ESCAPE_BEGIN)?;

            for code in 0..16usize {
                if self.font_style.enabled(code) {
                    f.write_fmt(format_args!(";{code}"))?;
                }
            }

            if self.fore.is_some() {
                f.write_fmt(format_args!(";{}", self.fore.unwrap().into_fore()))?;
            }

            if self.back.is_some() {
                f.write_fmt(format_args!(";{}", self.back.unwrap().into_back()))?;
            }

            f.write_str(ESCAPE_OVER)
        }
    }
}

impl<'a> AnsiString<'a> {
    pub fn new(content: &'a str) -> Self {
        Self {
            style: AnsiStyle::new(),
            is_vanilla: true,
            content,
        }
    }

    pub fn reset(self) -> Self {
        Self::new(self.content)
    }

    pub fn get_content(self) -> &'a str {
        self.content
    }
}