use bitflags::bitflags;
use crate::color::Color;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Attrs: u16 {
const BOLD = 0b0000_0000_0001;
const DIM = 0b0000_0000_0010;
const ITALIC = 0b0000_0000_0100;
const UNDERLINE = 0b0000_0000_1000;
const BLINK = 0b0000_0001_0000;
const BLINK_FAST = 0b0000_0010_0000;
const REVERSE = 0b0000_0100_0000;
const HIDDEN = 0b0000_1000_0000;
const STRIKETHROUGH = 0b0001_0000_0000;
}
}
#[must_use = "Style is a builder; assign or use the result"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Style {
pub(crate) fg: Option<Color>,
pub(crate) bg: Option<Color>,
pub(crate) attrs: Attrs,
}
impl Style {
pub const fn new() -> Self {
Self {
fg: None,
bg: None,
attrs: Attrs::empty(),
}
}
pub fn fg(mut self, color: Color) -> Self {
self.fg = Some(color);
self
}
pub fn bg(mut self, color: Color) -> Self {
self.bg = Some(color);
self
}
pub fn rgb(self, r: u8, g: u8, b: u8) -> Self {
self.fg(Color::Rgb(r, g, b))
}
pub fn hex(self, hex: &str) -> Self {
if let Some(c) = Color::from_hex(hex) {
self.fg(c)
} else {
debug_assert!(false, "invalid hex color: {hex}");
self
}
}
pub fn on_rgb(self, r: u8, g: u8, b: u8) -> Self {
self.bg(Color::Rgb(r, g, b))
}
pub fn on_hex(self, hex: &str) -> Self {
if let Some(c) = Color::from_hex(hex) {
self.bg(c)
} else {
debug_assert!(false, "invalid hex color: {hex}");
self
}
}
pub fn merge(mut self, other: Style) -> Self {
if other.fg.is_some() {
self.fg = other.fg;
}
if other.bg.is_some() {
self.bg = other.bg;
}
self.attrs |= other.attrs;
self
}
pub fn apply<T: std::fmt::Display>(self, val: T) -> crate::styled::Styled<T> {
crate::styled::Styled::new(val, self)
}
pub fn bold(mut self) -> Self {
self.attrs |= Attrs::BOLD;
self
}
pub fn dim(mut self) -> Self {
self.attrs |= Attrs::DIM;
self
}
pub fn italic(mut self) -> Self {
self.attrs |= Attrs::ITALIC;
self
}
pub fn underline(mut self) -> Self {
self.attrs |= Attrs::UNDERLINE;
self
}
pub fn blink(mut self) -> Self {
self.attrs |= Attrs::BLINK;
self
}
pub fn blink_fast(mut self) -> Self {
self.attrs |= Attrs::BLINK_FAST;
self
}
pub fn reverse(mut self) -> Self {
self.attrs |= Attrs::REVERSE;
self
}
pub fn hidden(mut self) -> Self {
self.attrs |= Attrs::HIDDEN;
self
}
pub fn strikethrough(mut self) -> Self {
self.attrs |= Attrs::STRIKETHROUGH;
self
}
pub fn black(self) -> Self {
self.fg(Color::BLACK)
}
pub fn red(self) -> Self {
self.fg(Color::RED)
}
pub fn green(self) -> Self {
self.fg(Color::GREEN)
}
pub fn yellow(self) -> Self {
self.fg(Color::YELLOW)
}
pub fn blue(self) -> Self {
self.fg(Color::BLUE)
}
pub fn magenta(self) -> Self {
self.fg(Color::MAGENTA)
}
pub fn cyan(self) -> Self {
self.fg(Color::CYAN)
}
pub fn white(self) -> Self {
self.fg(Color::WHITE)
}
pub fn bright_black(self) -> Self {
self.fg(Color::BRIGHT_BLACK)
}
pub fn bright_red(self) -> Self {
self.fg(Color::BRIGHT_RED)
}
pub fn bright_green(self) -> Self {
self.fg(Color::BRIGHT_GREEN)
}
pub fn bright_yellow(self) -> Self {
self.fg(Color::BRIGHT_YELLOW)
}
pub fn bright_blue(self) -> Self {
self.fg(Color::BRIGHT_BLUE)
}
pub fn bright_magenta(self) -> Self {
self.fg(Color::BRIGHT_MAGENTA)
}
pub fn bright_cyan(self) -> Self {
self.fg(Color::BRIGHT_CYAN)
}
pub fn bright_white(self) -> Self {
self.fg(Color::BRIGHT_WHITE)
}
pub fn on_black(self) -> Self {
self.bg(Color::BLACK)
}
pub fn on_red(self) -> Self {
self.bg(Color::RED)
}
pub fn on_green(self) -> Self {
self.bg(Color::GREEN)
}
pub fn on_yellow(self) -> Self {
self.bg(Color::YELLOW)
}
pub fn on_blue(self) -> Self {
self.bg(Color::BLUE)
}
pub fn on_magenta(self) -> Self {
self.bg(Color::MAGENTA)
}
pub fn on_cyan(self) -> Self {
self.bg(Color::CYAN)
}
pub fn on_white(self) -> Self {
self.bg(Color::WHITE)
}
pub fn on_bright_black(self) -> Self {
self.bg(Color::BRIGHT_BLACK)
}
pub fn on_bright_red(self) -> Self {
self.bg(Color::BRIGHT_RED)
}
pub fn on_bright_green(self) -> Self {
self.bg(Color::BRIGHT_GREEN)
}
pub fn on_bright_yellow(self) -> Self {
self.bg(Color::BRIGHT_YELLOW)
}
pub fn on_bright_blue(self) -> Self {
self.bg(Color::BRIGHT_BLUE)
}
pub fn on_bright_magenta(self) -> Self {
self.bg(Color::BRIGHT_MAGENTA)
}
pub fn on_bright_cyan(self) -> Self {
self.bg(Color::BRIGHT_CYAN)
}
pub fn on_bright_white(self) -> Self {
self.bg(Color::BRIGHT_WHITE)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_chaining() {
let s = Style::new().red().bold().underline();
assert_eq!(s.fg, Some(Color::RED));
assert!(s.attrs.contains(Attrs::BOLD));
assert!(s.attrs.contains(Attrs::UNDERLINE));
}
#[test]
fn merge_styles() {
let base = Style::new().red().bold();
let overlay = Style::new().blue().italic();
let merged = base.merge(overlay);
assert_eq!(merged.fg, Some(Color::BLUE));
assert!(merged.attrs.contains(Attrs::BOLD));
assert!(merged.attrs.contains(Attrs::ITALIC));
}
}