use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
macro_rules! auto_impl_ref_binop_trait {
(impl $trait_name:ident, $method:ident for $t:ty, $u:ty) => {
impl $trait_name<&$u> for $t {
type Output = <$t as $trait_name<$t>>::Output;
#[inline]
fn $method(self, rhs: &$u) -> Self::Output {
$trait_name::$method(self, *rhs)
}
}
impl $trait_name<$u> for &$t {
type Output = <$t as $trait_name<$t>>::Output;
#[inline]
fn $method(self, rhs: $u) -> Self::Output {
$trait_name::$method(*self, rhs)
}
}
impl $trait_name<&$u> for &$t {
type Output = <$t as $trait_name<$t>>::Output;
#[inline]
fn $method(self, rhs: &$u) -> Self::Output {
$trait_name::$method(*self, *rhs)
}
}
};
}
macro_rules! impl_assign_op_trait {
(
$trait:ident, $method:ident for $t:ty, $u:ty, using $used_trait:ident::$used_method:ident
) => {
impl $trait<$u> for $t {
#[inline]
fn $method(&mut self, other: $u) {
*self = $used_trait::$used_method(&*self, other);
}
}
impl $trait<&$u> for $t {
#[inline]
fn $method(&mut self, other: &$u) {
*self = $used_trait::$used_method(&*self, other);
}
}
};
}
const CLEARV: u8 = 0b0000_0000;
const BOLD: u8 = 0b0000_0001;
const UNDERLINE: u8 = 0b0000_0010;
const REVERSED: u8 = 0b0000_0100;
const ITALIC: u8 = 0b0000_1000;
const BLINK: u8 = 0b0001_0000;
const HIDDEN: u8 = 0b0010_0000;
const DIMMED: u8 = 0b0100_0000;
const STRIKETHROUGH: u8 = 0b1000_0000;
static STYLES: [(u8, Styles); 8] = [
(BOLD, Styles::Bold),
(DIMMED, Styles::Dimmed),
(UNDERLINE, Styles::Underline),
(REVERSED, Styles::Reversed),
(ITALIC, Styles::Italic),
(BLINK, Styles::Blink),
(HIDDEN, Styles::Hidden),
(STRIKETHROUGH, Styles::Strikethrough),
];
pub static CLEAR: Style = Style(CLEARV);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Style(u8);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(missing_docs)]
pub enum Styles {
Clear,
Bold,
Dimmed,
Underline,
Reversed,
Italic,
Blink,
Hidden,
Strikethrough,
}
impl Styles {
fn to_str<'a>(self) -> &'a str {
match self {
Styles::Clear => "", Styles::Bold => "1",
Styles::Dimmed => "2",
Styles::Italic => "3",
Styles::Underline => "4",
Styles::Blink => "5",
Styles::Reversed => "7",
Styles::Hidden => "8",
Styles::Strikethrough => "9",
}
}
fn to_u8(self) -> u8 {
match self {
Styles::Clear => CLEARV,
Styles::Bold => BOLD,
Styles::Dimmed => DIMMED,
Styles::Italic => ITALIC,
Styles::Underline => UNDERLINE,
Styles::Blink => BLINK,
Styles::Reversed => REVERSED,
Styles::Hidden => HIDDEN,
Styles::Strikethrough => STRIKETHROUGH,
}
}
fn from_u8(u: u8) -> Option<Vec<Styles>> {
if u == CLEARV {
return None;
}
let res: Vec<Styles> = STYLES
.iter()
.filter(|&(mask, _)| (0 != (u & mask)))
.map(|&(_, value)| value)
.collect();
if res.is_empty() {
None
} else {
Some(res)
}
}
}
impl BitAnd<Styles> for Styles {
type Output = Style;
fn bitand(self, rhs: Styles) -> Self::Output {
Style(self.to_u8() & rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Styles);
impl BitAnd<Style> for Styles {
type Output = Style;
fn bitand(self, rhs: Style) -> Self::Output {
Style(self.to_u8() & rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Style);
impl BitOr<Styles> for Styles {
type Output = Style;
fn bitor(self, rhs: Styles) -> Self::Output {
Style(self.to_u8() | rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Styles);
impl BitOr<Style> for Styles {
type Output = Style;
fn bitor(self, rhs: Style) -> Self::Output {
Style(self.to_u8() | rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Style);
impl BitXor<Styles> for Styles {
type Output = Style;
fn bitxor(self, rhs: Styles) -> Self::Output {
Style(self.to_u8() ^ rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Styles);
impl BitXor<Style> for Styles {
type Output = Style;
fn bitxor(self, rhs: Style) -> Self::Output {
Style(self.to_u8() ^ rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Style);
impl Not for Styles {
type Output = Style;
fn not(self) -> Self::Output {
Style(!self.to_u8())
}
}
impl Not for &Styles {
type Output = Style;
fn not(self) -> Self::Output {
Style(!self.to_u8())
}
}
impl Style {
pub fn contains(self, style: Styles) -> bool {
let s = style.to_u8();
self.0 & s == s
}
pub(crate) fn to_str(self) -> String {
let styles = Styles::from_u8(self.0).unwrap_or_default();
styles
.iter()
.map(|s| s.to_str())
.collect::<Vec<&str>>()
.join(";")
}
pub fn add(&mut self, two: Styles) {
self.0 |= two.to_u8();
}
pub fn remove(&mut self, two: Styles) {
self.0 &= !two.to_u8();
}
pub fn bold(mut self) -> Self {
self.add(Styles::Bold);
self
}
pub fn dimmed(mut self) -> Self {
self.add(Styles::Dimmed);
self
}
pub fn underline(mut self) -> Self {
self.add(Styles::Underline);
self
}
pub fn reversed(mut self) -> Self {
self.add(Styles::Reversed);
self
}
pub fn italic(mut self) -> Self {
self.add(Styles::Italic);
self
}
pub fn blink(mut self) -> Self {
self.add(Styles::Blink);
self
}
pub fn hidden(mut self) -> Self {
self.add(Styles::Hidden);
self
}
pub fn strikethrough(mut self) -> Self {
self.add(Styles::Strikethrough);
self
}
}
impl BitAnd<Style> for Style {
type Output = Style;
fn bitand(self, rhs: Style) -> Self::Output {
Style(self.0 & rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Style);
impl BitAnd<Styles> for Style {
type Output = Style;
fn bitand(self, rhs: Styles) -> Self::Output {
Style(self.0 & rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Styles);
impl BitOr<Style> for Style {
type Output = Style;
fn bitor(self, rhs: Style) -> Self::Output {
Style(self.0 | rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Style);
impl BitOr<Styles> for Style {
type Output = Style;
fn bitor(self, rhs: Styles) -> Self::Output {
Style(self.0 | rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Styles);
impl BitXor<Style> for Style {
type Output = Style;
fn bitxor(self, rhs: Style) -> Self::Output {
Style(self.0 ^ rhs.0)
}
}
auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Style);
impl BitXor<Styles> for Style {
type Output = Style;
fn bitxor(self, rhs: Styles) -> Self::Output {
Style(self.0 ^ rhs.to_u8())
}
}
auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Styles);
impl Not for Style {
type Output = Style;
fn not(self) -> Self::Output {
Style(!self.0)
}
}
impl Not for &Style {
type Output = Style;
fn not(self) -> Self::Output {
Style(!self.0)
}
}
impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Style, using BitAnd::bitand);
impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Styles, using BitAnd::bitand);
impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Style, using BitOr::bitor);
impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Styles, using BitOr::bitor);
impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Style, using BitXor::bitxor);
impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Styles, using BitXor::bitxor);
impl Default for Style {
fn default() -> Self {
CLEAR
}
}
impl From<Styles> for Style {
fn from(value: Styles) -> Self {
Style(value.to_u8())
}
}
impl From<&Styles> for Style {
fn from(value: &Styles) -> Self {
Style(value.to_u8())
}
}
impl FromIterator<Styles> for Style {
fn from_iter<T: IntoIterator<Item = Styles>>(iter: T) -> Self {
let mut style = Style::default();
for styles in iter.into_iter() {
style.add(styles);
}
style
}
}
#[cfg(test)]
mod tests {
use super::*;
mod u8_to_styles_invalid_is_none {
use super::super::Styles;
use super::super::CLEARV;
#[test]
fn empty_is_none() {
assert_eq!(None, Styles::from_u8(CLEARV));
}
}
mod u8_to_styles_isomorphism {
use super::super::Styles;
use super::super::{
BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
};
macro_rules! value_isomorph {
($name:ident, $value:expr) => {
#[test]
fn $name() {
let u = Styles::from_u8($value);
assert!(
u.is_some(),
"{}: Styles::from_u8 -> None",
stringify!($value)
);
let u = u.unwrap();
assert!(
u.len() == 1,
"{}: Styles::from_u8 found {} styles (expected 1)",
stringify!($value),
u.len()
);
assert!(
u[0].to_u8() == $value,
"{}: to_u8() doesn't match its const value",
stringify!($value)
);
}
};
}
value_isomorph!(bold, BOLD);
value_isomorph!(underline, UNDERLINE);
value_isomorph!(reversed, REVERSED);
value_isomorph!(italic, ITALIC);
value_isomorph!(blink, BLINK);
value_isomorph!(hidden, HIDDEN);
value_isomorph!(dimmed, DIMMED);
value_isomorph!(strikethrough, STRIKETHROUGH);
}
mod styles_combine_complex {
use super::super::Styles::*;
use super::super::{Style, Styles};
fn style_from_multiples(styles: &[Styles]) -> Style {
let mut res = Style(styles[0].to_u8());
for s in &styles[1..] {
res = Style(res.0 | s.to_u8());
}
res
}
macro_rules! test_aggreg {
($styles:expr, $expect:expr) => {{
let v = style_from_multiples($styles);
let r = Styles::from_u8(v.0).expect("should find styles");
assert_eq!(&$expect as &[Styles], &r[..])
}};
}
#[test]
fn aggreg1() {
let styles: &[Styles] = &[Bold, Bold, Bold];
test_aggreg!(styles, [Bold]);
}
#[test]
fn aggreg2() {
let styles: &[Styles] = &[Italic, Italic, Bold, Bold];
test_aggreg!(styles, [Bold, Italic]);
}
#[test]
fn aggreg3() {
let styles: &[Styles] = &[Bold, Italic, Bold];
test_aggreg!(styles, [Bold, Italic]);
}
macro_rules! test_combine {
($styles:expr) => {{
let v = style_from_multiples($styles);
let r = Styles::from_u8(v.0).expect("should find styles");
assert_eq!($styles, &r[..])
}};
}
#[test]
fn two1() {
let s: &[Styles] = &[Bold, Underline];
test_combine!(s);
}
#[test]
fn two2() {
let s: &[Styles] = &[Underline, Italic];
test_combine!(s);
}
#[test]
fn two3() {
let s: &[Styles] = &[Bold, Italic];
test_combine!(s);
}
#[test]
fn three1() {
let s: &[Styles] = &[Bold, Underline, Italic];
test_combine!(s);
}
#[test]
fn three2() {
let s: &[Styles] = &[Dimmed, Underline, Italic];
test_combine!(s);
}
#[test]
fn four() {
let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden];
test_combine!(s);
}
#[test]
fn five() {
let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden];
test_combine!(s);
}
#[test]
fn six() {
let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden];
test_combine!(s);
}
#[test]
fn all() {
let s: &[Styles] = &[
Bold,
Dimmed,
Underline,
Reversed,
Italic,
Blink,
Hidden,
Strikethrough,
];
test_combine!(s);
}
}
#[test]
fn test_style_contains() {
let mut style = Style(Styles::Bold.to_u8());
style.add(Styles::Italic);
assert!(style.contains(Styles::Bold));
assert!(style.contains(Styles::Italic));
assert!(!style.contains(Styles::Dimmed));
}
mod style_bitwise_logic {
use super::*;
macro_rules! check_impl {
($lh:expr, $method:path, $rh:expr => $res:expr) => {
assert_eq!($method($lh, $rh), $res);
assert_eq!($method(&$lh, $rh), $res);
assert_eq!($method($lh, &$rh), $res);
assert_eq!($method(&$lh, &$rh), $res);
};
}
macro_rules! check_impl_reflexive {
($lh:expr, $method:path, $rh:expr => $res:expr) => {
check_impl!($lh, $method, $rh => $res);
check_impl!($rh, $method, $lh => $res);
}
}
const TTABLE: (u8, u8) = (0b0101, 0b0011);
#[test]
fn binops() {
let tstyle_l = Style(TTABLE.0);
let tstyle_r = Style(TTABLE.1);
let and_res = Style(TTABLE.0 & TTABLE.1);
let or_res = Style(TTABLE.0 | TTABLE.1);
let xor_res = Style(TTABLE.0 ^ TTABLE.1);
check_impl!(tstyle_l, BitAnd::bitand, tstyle_r => and_res);
check_impl!(tstyle_l, BitOr::bitor, tstyle_r => or_res);
check_impl!(tstyle_l, BitXor::bitxor, tstyle_r => xor_res);
}
#[test]
fn binops_with_styles() {
let bold_underline = Style(0b0011);
check_impl_reflexive!(
bold_underline,
BitAnd::bitand,
Styles::Bold
=> Style(0b0000_0001)
);
check_impl_reflexive!(
bold_underline,
BitOr::bitor,
Styles::Reversed
=> Style(0b0000_0111)
);
check_impl_reflexive!(
bold_underline,
BitXor::bitxor,
Styles::Underline
=> Style(0b0000_0001)
);
}
#[test]
fn not() {
let not_bold = !Style(BOLD);
assert!(!not_bold.contains(Styles::Bold));
assert!(not_bold.contains(Styles::Strikethrough));
assert_eq!(!Style(0b0011_0101), Style(0b1100_1010));
}
#[test]
fn assign_ops() {
let original_style = Style(0b0011);
let op_style = Style(0b0101);
let mut style = original_style;
style &= op_style;
assert_eq!(style, Style(0b0001));
style = original_style;
style |= op_style;
assert_eq!(style, Style(0b0111));
style = original_style;
style ^= op_style;
assert_eq!(style, Style(0b0110));
}
#[test]
fn assign_ops_with_styles() {
let original_style = Style(0b0011);
let mut style = original_style;
style &= Styles::Bold;
assert_eq!(style, Style(0b0001));
style = original_style;
style |= Styles::Reversed;
assert_eq!(style, Style(0b0111));
style = original_style;
style ^= Styles::Bold;
assert_eq!(style, Style(0b0010));
}
#[test]
fn styles_binops() {
check_impl!(
Styles::Bold,
BitAnd::bitand,
Styles::Bold
=> Style(0b0000_0001)
);
assert_eq!(Styles::Bold & Styles::Underline, Style(0b0000_0000));
check_impl!(
Styles::Bold,
BitOr::bitor,
Styles::Underline
=> Style(0b0000_0011)
);
assert_eq!(Styles::Bold | Styles::Bold, Style(0b0000_0001));
check_impl!(
Styles::Bold,
BitXor::bitxor,
Styles::Underline
=> Style(0b0000_0011)
);
assert_eq!(Styles::Bold ^ Styles::Bold, Style(0b0000_0000));
}
#[test]
fn styles_not() {
let not_bold = !Styles::Bold;
assert_eq!(not_bold, Style(!BOLD));
}
}
}