use std::hash::{Hash, Hasher};
use std::fmt::{self, Display};
use std::ops::BitOr;
use {Paint, Color};
#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
pub struct Property(u8);
impl Property {
pub const BOLD: Self = Property(1 << 0);
pub const DIMMED: Self = Property(1 << 1);
pub const ITALIC: Self = Property(1 << 2);
pub const UNDERLINE: Self = Property(1 << 3);
pub const BLINK: Self = Property(1 << 4);
pub const INVERT: Self = Property(1 << 5);
pub const HIDDEN: Self = Property(1 << 6);
pub const STRIKETHROUGH: Self = Property(1 << 7);
#[inline(always)]
pub fn contains(self, other: Property) -> bool {
(other.0 & self.0) == other.0
}
#[inline(always)]
pub fn set(&mut self, other: Property) {
self.0 |= other.0;
}
#[inline(always)]
pub fn iter(self) -> Iter {
Iter { index: 0, properties: self }
}
}
impl BitOr for Property {
type Output = Self;
#[inline(always)]
fn bitor(self, rhs: Self) -> Self {
Property(self.0 | rhs.0)
}
}
pub struct Iter {
index: u8,
properties: Property,
}
impl Iterator for Iter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
while self.index < 8 {
let index = self.index;
self.index += 1;
if self.properties.contains(Property(1 << index)) {
return Some(index as usize);
}
}
None
}
}
#[repr(packed)]
#[derive(Default, Debug, Eq, Ord, PartialOrd, Copy, Clone)]
pub struct Style {
pub(crate) foreground: Color,
pub(crate) background: Color,
pub(crate) properties: Property,
pub(crate) masked: bool,
pub(crate) wrap: bool,
}
impl PartialEq for Style {
fn eq(&self, other: &Style) -> bool {
self.foreground == other.foreground
&& self.background == other.background
&& self.properties == other.properties
}
}
impl Hash for Style {
fn hash<H: Hasher>(&self, state: &mut H) {
self.foreground.hash(state);
self.background.hash(state);
self.properties.hash(state);
}
}
macro_rules! checker_for {
($($name:ident ($fn_name:ident): $property:ident),*) => ($(
#[doc = concat!(
"Returns `true` if the _", stringify!($name), "_ property is set on `self`.\n",
"```rust\n",
"use yansi::Style;\n",
"\n",
"let plain = Style::default();\n",
"assert!(!plain.", stringify!($fn_name), "());\n",
"\n",
"let styled = plain.", stringify!($name), "();\n",
"assert!(styled.", stringify!($fn_name), "());\n",
"```\n"
)]
#[inline]
pub fn $fn_name(&self) -> bool {
self.properties.contains(Property::$property)
}
)*)
}
#[inline]
fn write_spliced<T: Display>(c: &mut bool, f: &mut fmt::Write, t: T) -> fmt::Result {
if *c {
write!(f, ";{}", t)
} else {
*c = true;
write!(f, "{}", t)
}
}
impl Style {
#[inline]
pub fn new(color: Color) -> Style {
Self::default().fg(color)
}
#[inline]
pub fn fg(mut self, color: Color) -> Style {
self.foreground = color;
self
}
#[inline]
pub fn bg(mut self, color: Color) -> Style {
self.background = color;
self
}
#[inline]
pub fn mask(mut self) -> Style {
self.masked = true;
self
}
#[inline]
pub fn wrap(mut self) -> Style {
self.wrap = true;
self
}
style_builder_for!(Style, |style| style.properties,
bold: BOLD, dimmed: DIMMED, italic: ITALIC,
underline: UNDERLINE, blink: BLINK, invert: INVERT,
hidden: HIDDEN, strikethrough: STRIKETHROUGH);
#[inline]
pub fn paint<T>(self, item: T) -> Paint<T> {
Paint::new(item).with_style(self)
}
#[inline]
pub fn fg_color(&self) -> Color {
self.foreground
}
#[inline]
pub fn bg_color(&self) -> Color {
self.background
}
#[inline]
pub fn is_masked(&self) -> bool {
self.masked
}
#[inline]
pub fn is_wrapping(&self) -> bool {
self.wrap
}
checker_for!(bold (is_bold): BOLD, dimmed (is_dimmed): DIMMED,
italic (is_italic): ITALIC, underline (is_underline): UNDERLINE,
blink (is_blink): BLINK, invert (is_invert): INVERT,
hidden (is_hidden): HIDDEN,
strikethrough (is_strikethrough): STRIKETHROUGH);
#[inline(always)]
fn is_plain(&self) -> bool {
self == &Style::default()
}
pub fn fmt_prefix(&self, f: &mut fmt::Write) -> fmt::Result {
if self.is_plain() {
return Ok(());
}
let mut splice = false;
write!(f, "\x1B[")?;
for i in self.properties.iter() {
let k = if i >= 5 { i + 2 } else { i + 1 };
write_spliced(&mut splice, f, k)?;
}
if self.background != Color::Unset {
write_spliced(&mut splice, f, "4")?;
self.background.ascii_fmt(f)?;
}
if self.foreground != Color::Unset {
write_spliced(&mut splice, f, "3")?;
self.foreground.ascii_fmt(f)?;
}
write!(f, "m")
}
pub fn fmt_suffix(&self, f: &mut fmt::Write) -> fmt::Result {
if self.is_plain() {
return Ok(());
}
write!(f, "\x1B[0m")
}
}