ansiconst 0.2.1

Library for declaring nestable ANSI styles in const context
Documentation
use crate::{Color, Coloree, Toggle, ToggleColor};
use crate::write::{compile_time, run_time};
use crate::introspect::Attr;
use bitflags::bitflags;
use std::fmt;

bitflags! {
    #[derive(PartialEq, Eq, Clone, Copy, fmt::Debug)]
    pub(super) struct Attrs: u8 {
        const Fg = 1 << 0;
        const Bg = 1 << 1;
    }
}

impl Attrs {
    #[inline]
    pub(super) const fn contains_coloree(&self, coloree: Coloree) -> bool {
        self.contains(Self::from_coloree(coloree))
    }

    #[inline]
    const fn from_coloree(coloree: Coloree) -> Self {
        match coloree {
            Coloree::Text       => Self::Fg,
            Coloree::Background => Self::Bg,
        }
    }
}

#[derive(PartialEq, Eq, Clone, Copy)]
pub(super) struct Colors { fg: Option<ToggleColor>, bg: Option<ToggleColor> }

impl Colors {
    #[inline]
    pub(super) const fn from_color(color: Color, toggle: Toggle, coloree: Coloree) -> Self {
        let is_color = match toggle {
            Toggle::Reset => ToggleColor::Reset,
            Toggle::Set   => ToggleColor::Set(color),
        };
        Self::new(is_color, coloree)
    }

    #[inline]
    const fn new(color: ToggleColor, coloree: Coloree) -> Self {
        match coloree {
            Coloree::Text       => Self { fg: Some(color), bg: None        },
            Coloree::Background => Self { fg: None,        bg: Some(color) },
        }
    }
    #[inline]
    pub(super) const fn is_empty(&self) -> bool { self.fg.is_none() && self.bg.is_none() }
    #[inline]
    pub(super) const fn is_reset(&self) -> bool {
        match (self.fg, self.bg) {
            (Some(ToggleColor::Reset), Some(ToggleColor::Reset)) => true,
            _ => false,
        }
    }
    #[inline]
    pub(super) const fn empty() -> Self { Self { fg: None, bg: None } }
    #[inline]
    pub(super) const fn reset() -> Self { Self { fg: Some(ToggleColor::Reset), bg: Some(ToggleColor::Reset) } }
    #[inline]
    pub(super) const fn get_color(&self, coloree: Coloree) -> Option<Attr<Color>> {
        let is_color = match coloree {
            Coloree::Text       => self.fg,
            Coloree::Background => self.bg,
        };
        match is_color {
            None => None,
            Some(ToggleColor::Reset)  => Some(Attr::new_color(Color::Black, Toggle::Reset, coloree)),
            Some(ToggleColor::Set(c)) => Some(Attr::new_color(c, Toggle::Set, coloree)),
        }
    }
    #[inline]
    const fn not_fg(&self) -> Option<ToggleColor> {
        match self.fg {
            None | Some(ToggleColor::Reset) => None,
            _ => Some(ToggleColor::Reset),
        }
    }
    #[inline]
    const fn not_bg(&self) -> Option<ToggleColor> {
        match self.bg {
            None | Some(ToggleColor::Reset) => None,
            _ => Some(ToggleColor::Reset),
        }
    }
    #[inline]
    pub(super) const fn add(&self, other: Self) -> Self {
        Self {
            fg: if other.fg.is_none() { self.fg } else { other.fg },
            bg: if other.bg.is_none() { self.bg } else { other.bg },
        }
    }
    #[inline]
    pub(super) fn transition(&self, to_other: Self) -> Self {
        Self {
            fg: if to_other.fg.is_none() { self.not_fg() } else if self.fg == to_other.fg { None } else { to_other.fg },
            bg: if to_other.bg.is_none() { self.not_bg() } else if self.bg == to_other.bg { None } else { to_other.bg },
        }
    }
    #[inline]
    pub(super) const fn not(&self) -> Self {
        Self {
            fg: self.not_fg(),
            bg: self.not_bg(),
        }
    }
    #[inline]
    pub(super) const fn only(&self) -> Self {
        Self {
            fg: if self.fg.is_none() { Some(ToggleColor::Reset) } else { self.fg },
            bg: if self.bg.is_none() { Some(ToggleColor::Reset) } else { self.bg },
        }
    }
    #[inline]
    pub(super) const fn remove(&self, attrs: Attrs) -> Self {
        Self {
            fg: if attrs.intersects(Attrs::Fg) { None } else { self.fg },
            bg: if attrs.intersects(Attrs::Bg) { None } else { self.bg },
        }
    }
    #[inline]
    pub(super) const fn attrs(&self) -> Attrs {
        let fg = if self.fg.is_none() { Attrs::empty() } else { Attrs::Fg };
        let bg = if self.bg.is_none() { Attrs::empty() } else { Attrs::Bg };
        fg.union(bg)
    }

    #[inline]
    pub(super) fn write(&self, w: &mut run_time::Formatter<'_,'_>, toggle: Toggle) -> fmt::Result {
        match (toggle, self.fg) {
            (Toggle::Reset, Some(ToggleColor::Reset))  => w.write_color(Coloree::Text, ToggleColor::Reset)?,
            (Toggle::Set,   Some(ToggleColor::Set(c))) => w.write_color(Coloree::Text, ToggleColor::Set(c))?,
            _ => (),
        }
        match (toggle, self.bg) {
            (Toggle::Reset, Some(ToggleColor::Reset))  => w.write_color(Coloree::Background, ToggleColor::Reset)?,
            (Toggle::Set,   Some(ToggleColor::Set(c))) => w.write_color(Coloree::Background, ToggleColor::Set(c))?,
            _ => (),
        }
        Ok(())
    }
    #[inline]
    pub(super) const fn write_const(&self, mut w: compile_time::Writer, toggle: Toggle) -> compile_time::Writer {
        w = match (toggle, self.fg) {
            (Toggle::Reset, Some(ToggleColor::Reset))  => w.write_color(Coloree::Text, ToggleColor::Reset),
            (Toggle::Set,   Some(ToggleColor::Set(c))) => w.write_color(Coloree::Text, ToggleColor::Set(c)),
            _ => w,
        };
        w = match (toggle, self.bg) {
            (Toggle::Reset, Some(ToggleColor::Reset))  => w.write_color(Coloree::Background, ToggleColor::Reset),
            (Toggle::Set,   Some(ToggleColor::Set(c))) => w.write_color(Coloree::Background, ToggleColor::Set(c)),
            _ => w,
        };
        w
    }
}

impl From<&Color> for Colors {
    fn from(fg: &Color) -> Self { Self::from_color(*fg, Toggle::Set, Coloree::Text) }
}
impl From<Color> for Colors {
    fn from(fg: Color) -> Self { Self::from_color(fg, Toggle::Set, Coloree::Text) }
}