use enum_map::{Enum, EnumMap};
use enumset::{EnumSet, EnumSetType};
use std::str::FromStr;
pub type ConcreteEffects = EnumSet<Effect>;
#[allow(clippy::derived_hash_with_manual_eq)] #[derive(EnumSetType, Enum, Debug, Hash)]
pub enum Effect {
Simple,
Reverse,
Dim,
Bold,
Italic,
Strikethrough,
Underline,
Blink,
}
impl Effect {
pub(crate) const fn ordinal(self) -> usize {
match self {
Effect::Simple => 0,
Effect::Reverse => 1,
Effect::Dim => 2,
Effect::Bold => 3,
Effect::Italic => 4,
Effect::Strikethrough => 5,
Effect::Underline => 6,
Effect::Blink => 7,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct Effects {
pub statuses: EnumMap<Effect, EffectStatus>,
}
impl Default for Effects {
fn default() -> Self {
Self::empty()
}
}
impl From<ConcreteEffects> for Effects {
fn from(other: ConcreteEffects) -> Self {
let mut result = Self::default();
for effect in other {
result.statuses[effect] = EffectStatus::OppositeParent;
}
result
}
}
impl Effects {
pub const EMPTY: Self = Effects::empty();
pub const fn empty() -> Self {
let statuses = [EffectStatus::InheritParent; Effect::LENGTH];
Self {
statuses: EnumMap::from_array(statuses),
}
}
pub fn remove(&mut self, effect: Effect) {
self.statuses[effect] = EffectStatus::InheritParent;
}
pub fn insert(&mut self, effect: Effect) {
self.statuses[effect] = EffectStatus::OppositeParent;
}
const fn status_for(i: usize, effect: Effect) -> EffectStatus {
if i == effect.ordinal() {
EffectStatus::OppositeParent
} else {
EffectStatus::InheritParent
}
}
pub const fn only(effect: Effect) -> Self {
let statuses = [
Self::status_for(0, effect),
Self::status_for(1, effect),
Self::status_for(2, effect),
Self::status_for(3, effect),
Self::status_for(4, effect),
Self::status_for(5, effect),
Self::status_for(6, effect),
Self::status_for(7, effect),
];
Self {
statuses: EnumMap::from_array(statuses),
}
}
pub fn resolve(&self, old: ConcreteEffects) -> ConcreteEffects {
let mut result = ConcreteEffects::default();
for (effect, status) in self.statuses {
if matches!(
(status, old.contains(effect)),
(EffectStatus::On, _)
| (EffectStatus::InheritParent, true)
| (EffectStatus::OppositeParent, false)
) {
result.insert(effect);
}
}
result
}
pub fn merge(mut old: Self, new: Self) -> Self {
for (effect, status) in new.statuses {
old.statuses[effect] = EffectStatus::merge(old.statuses[effect], status);
}
old
}
}
impl std::ops::Index<Effect> for Effects {
type Output = EffectStatus;
fn index(&self, index: Effect) -> &Self::Output {
&self.statuses[index]
}
}
impl std::ops::IndexMut<Effect> for Effects {
fn index_mut(&mut self, index: Effect) -> &mut Self::Output {
&mut self.statuses[index]
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum EffectStatus {
On,
Off,
InheritParent,
OppositeParent,
}
impl EffectStatus {
pub const fn swap(self) -> Self {
match self {
EffectStatus::On => EffectStatus::Off,
EffectStatus::Off => EffectStatus::On,
EffectStatus::InheritParent => EffectStatus::OppositeParent,
EffectStatus::OppositeParent => EffectStatus::InheritParent,
}
}
pub const fn merge(old: Self, new: Self) -> Self {
match new {
EffectStatus::On => EffectStatus::On,
EffectStatus::Off => EffectStatus::Off,
EffectStatus::InheritParent => old,
EffectStatus::OppositeParent => old.swap(),
}
}
}
impl FromStr for EffectStatus {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"On" | "on" | "true" => Self::On,
"Off" | "off" | "false" => Self::Off,
"InheritParent" | "inherit_parent" => Self::InheritParent,
"OppositeParent" | "opposite_parent" => Self::OppositeParent,
_ => return Err(()),
})
}
}
impl FromStr for Effect {
type Err = super::NoSuchColor;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"Simple" | "simple" => Effect::Simple,
"Reverse" | "reverse" => Effect::Reverse,
"Dim" | "dim" => Effect::Dim,
"Bold" | "bold" => Effect::Bold,
"Italic" | "italic" => Effect::Italic,
"Strikethrough" | "strikethrough" => Effect::Strikethrough,
"Underline" | "underline" => Effect::Underline,
"Blink" | "blink" => Effect::Blink,
_ => return Err(super::NoSuchColor),
})
}
}