use crossterm::style::Attribute;
use std::ops::{BitOr, BitOrAssign};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub struct Modifier(u16);
impl Modifier {
pub const NONE: Self = Self(0);
pub const BOLD: Self = Self(1 << 0);
pub const DIM: Self = Self(1 << 1);
pub const ITALIC: Self = Self(1 << 2);
pub const UNDERLINED: Self = Self(1 << 3);
pub const SLOW_BLINK: Self = Self(1 << 4);
pub const RAPID_BLINK: Self = Self(1 << 5);
pub const REVERSED: Self = Self(1 << 6);
pub const HIDDEN: Self = Self(1 << 7);
pub const CROSSED_OUT: Self = Self(1 << 8);
pub const fn empty() -> Self {
Self::NONE
}
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
pub const fn contains(&self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
pub fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
pub fn remove(&mut self, other: Self) {
self.0 &= !other.0;
}
pub fn attributes(&self) -> Vec<Attribute> {
let mut attrs = Vec::new();
if self.contains(Self::BOLD) {
attrs.push(Attribute::Bold);
}
if self.contains(Self::DIM) {
attrs.push(Attribute::Dim);
}
if self.contains(Self::ITALIC) {
attrs.push(Attribute::Italic);
}
if self.contains(Self::UNDERLINED) {
attrs.push(Attribute::Underlined);
}
if self.contains(Self::SLOW_BLINK) {
attrs.push(Attribute::SlowBlink);
}
if self.contains(Self::RAPID_BLINK) {
attrs.push(Attribute::RapidBlink);
}
if self.contains(Self::REVERSED) {
attrs.push(Attribute::Reverse);
}
if self.contains(Self::HIDDEN) {
attrs.push(Attribute::Hidden);
}
if self.contains(Self::CROSSED_OUT) {
attrs.push(Attribute::CrossedOut);
}
attrs
}
}
impl BitOr for Modifier {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
self.union(rhs)
}
}
impl BitOrAssign for Modifier {
fn bitor_assign(&mut self, rhs: Self) {
self.insert(rhs);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_modifier_empty() {
let m = Modifier::empty();
assert!(m.is_empty());
assert!(!m.contains(Modifier::BOLD));
}
#[test]
fn test_modifier_union() {
let m = Modifier::BOLD | Modifier::ITALIC;
assert!(m.contains(Modifier::BOLD));
assert!(m.contains(Modifier::ITALIC));
assert!(!m.contains(Modifier::UNDERLINED));
}
#[test]
fn test_modifier_insert_remove() {
let mut m = Modifier::BOLD;
m.insert(Modifier::ITALIC);
assert!(m.contains(Modifier::BOLD));
assert!(m.contains(Modifier::ITALIC));
m.remove(Modifier::BOLD);
assert!(!m.contains(Modifier::BOLD));
assert!(m.contains(Modifier::ITALIC));
}
#[test]
fn test_modifier_attributes() {
let m = Modifier::BOLD | Modifier::UNDERLINED;
let attrs = m.attributes();
assert_eq!(attrs.len(), 2);
assert!(attrs.contains(&Attribute::Bold));
assert!(attrs.contains(&Attribute::Underlined));
}
}