1use enum_map::{Enum, EnumMap};
2use enumset::{EnumSet, EnumSetType};
3use std::str::FromStr;
4
5pub type ConcreteEffects = EnumSet<Effect>;
9
10#[allow(clippy::derived_hash_with_manual_eq)] #[derive(EnumSetType, Enum, Debug, Hash)]
13pub enum Effect {
14 Simple,
16
17 Reverse,
19
20 Dim,
22
23 Bold,
25
26 Italic,
28
29 Strikethrough,
31
32 Underline,
34
35 Blink,
37}
38
39impl Effect {
40 pub(crate) const fn ordinal(self) -> usize {
47 match self {
48 Effect::Simple => 0,
49 Effect::Reverse => 1,
50 Effect::Dim => 2,
51 Effect::Bold => 3,
52 Effect::Italic => 4,
53 Effect::Strikethrough => 5,
54 Effect::Underline => 6,
55 Effect::Blink => 7,
56 }
57 }
58}
59
60#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
64pub struct Effects {
65 pub statuses: EnumMap<Effect, EffectStatus>,
67}
68
69impl Default for Effects {
70 fn default() -> Self {
71 Self::empty()
72 }
73}
74
75impl From<ConcreteEffects> for Effects {
76 fn from(other: ConcreteEffects) -> Self {
77 let mut result = Self::default();
78 for effect in other {
79 result.statuses[effect] = EffectStatus::OppositeParent;
80 }
81 result
82 }
83}
84
85impl Effects {
86 pub const EMPTY: Self = Effects::empty();
88
89 pub const fn empty() -> Self {
93 let statuses = [EffectStatus::InheritParent; Effect::LENGTH];
94 Self {
95 statuses: EnumMap::from_array(statuses),
96 }
97 }
98
99 pub fn remove(&mut self, effect: Effect) {
101 self.statuses[effect] = EffectStatus::InheritParent;
102 }
103
104 pub fn insert(&mut self, effect: Effect) {
106 self.statuses[effect] = EffectStatus::OppositeParent;
107 }
108
109 const fn status_for(i: usize, effect: Effect) -> EffectStatus {
111 if i == effect.ordinal() {
112 EffectStatus::OppositeParent
113 } else {
114 EffectStatus::InheritParent
115 }
116 }
117
118 pub const fn only(effect: Effect) -> Self {
122 let statuses = [
124 Self::status_for(0, effect),
125 Self::status_for(1, effect),
126 Self::status_for(2, effect),
127 Self::status_for(3, effect),
128 Self::status_for(4, effect),
129 Self::status_for(5, effect),
130 Self::status_for(6, effect),
131 Self::status_for(7, effect),
132 ];
133
134 Self {
135 statuses: EnumMap::from_array(statuses),
136 }
137 }
138
139 pub fn resolve(&self, old: ConcreteEffects) -> ConcreteEffects {
141 let mut result = ConcreteEffects::default();
142 for (effect, status) in self.statuses {
143 if matches!(
144 (status, old.contains(effect)),
145 (EffectStatus::On, _)
146 | (EffectStatus::InheritParent, true)
147 | (EffectStatus::OppositeParent, false)
148 ) {
149 result.insert(effect);
150 }
151 }
152 result
153 }
154
155 pub fn merge(mut old: Self, new: Self) -> Self {
157 for (effect, status) in new.statuses {
158 old.statuses[effect] = EffectStatus::merge(old.statuses[effect], status);
159 }
160 old
161 }
162}
163
164impl std::ops::Index<Effect> for Effects {
165 type Output = EffectStatus;
166
167 fn index(&self, index: Effect) -> &Self::Output {
168 &self.statuses[index]
169 }
170}
171
172impl std::ops::IndexMut<Effect> for Effects {
173 fn index_mut(&mut self, index: Effect) -> &mut Self::Output {
174 &mut self.statuses[index]
175 }
176}
177
178#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
180pub enum EffectStatus {
181 On,
183
184 Off,
186
187 InheritParent,
189
190 OppositeParent,
192}
193
194impl EffectStatus {
195 pub const fn swap(self) -> Self {
200 match self {
201 EffectStatus::On => EffectStatus::Off,
202 EffectStatus::Off => EffectStatus::On,
203 EffectStatus::InheritParent => EffectStatus::OppositeParent,
204 EffectStatus::OppositeParent => EffectStatus::InheritParent,
205 }
206 }
207
208 pub const fn merge(old: Self, new: Self) -> Self {
210 match new {
211 EffectStatus::On => EffectStatus::On,
212 EffectStatus::Off => EffectStatus::Off,
213 EffectStatus::InheritParent => old,
214 EffectStatus::OppositeParent => old.swap(),
215 }
216 }
217}
218impl FromStr for EffectStatus {
219 type Err = ();
220
221 fn from_str(s: &str) -> Result<Self, Self::Err> {
222 Ok(match s {
223 "On" | "on" | "true" => Self::On,
224 "Off" | "off" | "false" => Self::Off,
225 "InheritParent" | "inherit_parent" => Self::InheritParent,
226 "OppositeParent" | "opposite_parent" => Self::OppositeParent,
227 _ => return Err(()),
228 })
229 }
230}
231
232impl FromStr for Effect {
233 type Err = super::NoSuchColor;
234
235 fn from_str(s: &str) -> Result<Self, Self::Err> {
236 Ok(match s {
237 "Simple" | "simple" => Effect::Simple,
238 "Reverse" | "reverse" => Effect::Reverse,
239 "Dim" | "dim" => Effect::Dim,
240 "Bold" | "bold" => Effect::Bold,
241 "Italic" | "italic" => Effect::Italic,
242 "Strikethrough" | "strikethrough" => Effect::Strikethrough,
243 "Underline" | "underline" => Effect::Underline,
244 "Blink" | "blink" => Effect::Blink,
245 _ => return Err(super::NoSuchColor),
246 })
247 }
248}