use derive_new::new;
use std::{fmt::Debug, hash::Hash};
use indexmap::IndexSet;
#[derive(Copy, Clone)]
pub struct Attribute {
attr: console::Attribute,
}
impl Debug for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.attr {
console::Attribute::Bold => "bold",
console::Attribute::Dim => "dim",
console::Attribute::Italic => "italic",
console::Attribute::Underlined => "underlined",
console::Attribute::Blink => "blink",
console::Attribute::Reverse => "reverse",
console::Attribute::Hidden => "hidden",
}
)
}
}
impl PartialOrd for Attribute {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.attr.partial_cmp(&other.attr)
}
}
impl Ord for Attribute {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.attr.cmp(&other.attr)
}
}
impl PartialEq for Attribute {
fn eq(&self, other: &Self) -> bool {
self.attr == other.attr
}
}
impl Eq for Attribute {}
impl Hash for Attribute {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self.attr {
console::Attribute::Bold => 0.hash(state),
console::Attribute::Dim => 1.hash(state),
console::Attribute::Italic => 2.hash(state),
console::Attribute::Underlined => 3.hash(state),
console::Attribute::Blink => 4.hash(state),
console::Attribute::Reverse => 5.hash(state),
console::Attribute::Hidden => 6.hash(state),
}
}
}
impl Into<Attribute> for console::Attribute {
fn into(self) -> Attribute {
Attribute { attr: self }
}
}
#[derive(Clone, new)]
pub struct Style {
#[new(value = "None")]
fg: Option<console::Color>,
#[new(value = "None")]
bg: Option<console::Color>,
#[new(default)]
attrs: IndexSet<Attribute>,
}
impl Default for Style {
fn default() -> Self {
Style::new()
}
}
impl Style {
pub fn apply_to<D>(&self, fragment: D) -> console::StyledObject<D> {
let style: console::Style = self.into();
style.apply_to(fragment)
}
pub fn fg(mut self, color: impl Into<console::Color>) -> Style {
self.fg = Some(color.into());
self
}
pub fn bg(mut self, color: impl Into<console::Color>) -> Style {
self.bg = Some(color.into());
self
}
pub fn attr(mut self, attr: impl Into<Attribute>) -> Style {
self.attrs.insert(attr.into());
self
}
}
impl<'a> From<&'a Style> for console::Style {
fn from(style: &'a Style) -> Self {
let style = style.clone();
style.into()
}
}
impl From<Style> for console::Style {
fn from(style: Style) -> Self {
let mut console_style = console::Style::new();
if let Some(fg) = style.fg {
console_style = console_style.fg(fg);
}
if let Some(bg) = style.bg {
console_style = console_style.bg(bg);
}
for attr in style.attrs {
console_style = console_style.attr(attr.attr);
}
console_style
}
}
impl Debug for Style {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { fg, bg, attrs } = self;
let mut desc = String::new();
let mut short = true;
match (fg, bg) {
(Some(fg), None) => {
desc.push_str(&format!("{:?}", fg));
}
(None, Some(bg)) => {
desc.push_str(&format!("normal on {:?}", bg));
short = false;
}
(None, None) => {
desc.push_str("normal");
}
(Some(fg), Some(bg)) => {
desc.push_str(&format!("{:?} on {:?}", fg, bg));
short = false;
}
}
let mut debug_attrs = String::new();
for attr in itertools::sorted(attrs.iter()) {
debug_attrs.push_str(", ");
debug_attrs.push_str(&format!("{:?}", attr));
}
if !debug_attrs.is_empty() {
short = false;
}
if short {
write!(f, "{}", desc)
} else {
write!(f, "[{}{}]", desc, debug_attrs)
}
}
}