anstyle/
effect.rs
1#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Effects(u16);
10
11impl Effects {
12 const PLAIN: Self = Effects(0);
14
15 #[allow(missing_docs)]
16 pub const BOLD: Self = Effects(1 << 0);
17 #[allow(missing_docs)]
18 pub const DIMMED: Self = Effects(1 << 1);
19 pub const ITALIC: Self = Effects(1 << 2);
21 pub const UNDERLINE: Self = Effects(1 << 3);
23 #[allow(missing_docs)]
24 pub const DOUBLE_UNDERLINE: Self = Effects(1 << 4);
25 #[allow(missing_docs)]
26 pub const CURLY_UNDERLINE: Self = Effects(1 << 5);
27 #[allow(missing_docs)]
28 pub const DOTTED_UNDERLINE: Self = Effects(1 << 6);
29 #[allow(missing_docs)]
30 pub const DASHED_UNDERLINE: Self = Effects(1 << 7);
31 #[allow(missing_docs)]
32 pub const BLINK: Self = Effects(1 << 8);
33 pub const INVERT: Self = Effects(1 << 9);
35 #[allow(missing_docs)]
36 pub const HIDDEN: Self = Effects(1 << 10);
37 pub const STRIKETHROUGH: Self = Effects(1 << 11);
39
40 #[inline]
48 pub const fn new() -> Self {
49 Self::PLAIN
50 }
51
52 #[inline]
64 pub const fn is_plain(self) -> bool {
65 self.0 == Self::PLAIN.0
66 }
67
68 #[inline(always)]
80 pub const fn contains(self, other: Effects) -> bool {
81 (other.0 & self.0) == other.0
82 }
83
84 #[inline(always)]
96 #[must_use]
97 pub const fn insert(mut self, other: Effects) -> Self {
98 self.0 |= other.0;
99 self
100 }
101
102 #[inline(always)]
112 #[must_use]
113 pub const fn remove(mut self, other: Effects) -> Self {
114 self.0 &= !other.0;
115 self
116 }
117
118 #[inline(always)]
125 #[must_use]
126 pub const fn clear(self) -> Self {
127 Self::new()
128 }
129
130 #[inline]
139 #[must_use]
140 pub const fn set(self, other: Self, enable: bool) -> Self {
141 if enable {
142 self.insert(other)
143 } else {
144 self.remove(other)
145 }
146 }
147
148 #[inline(always)]
150 pub fn iter(self) -> EffectIter {
151 EffectIter {
152 index: 0,
153 effects: self,
154 }
155 }
156
157 #[inline(always)]
159 pub(crate) fn index_iter(self) -> EffectIndexIter {
160 EffectIndexIter {
161 index: 0,
162 effects: self,
163 }
164 }
165
166 #[inline]
168 pub fn render(self) -> impl core::fmt::Display + Copy {
169 EffectsDisplay(self)
170 }
171
172 #[inline]
173 #[cfg(feature = "std")]
174 pub(crate) fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
175 for index in self.index_iter() {
176 write.write_all(METADATA[index].escape.as_bytes())?;
177 }
178 Ok(())
179 }
180}
181
182impl core::fmt::Debug for Effects {
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 write!(f, "Effects(")?;
194 for (i, index) in self.index_iter().enumerate() {
195 if i != 0 {
196 write!(f, " | ")?;
197 }
198 write!(f, "{}", METADATA[index].name)?;
199 }
200 write!(f, ")")?;
201 Ok(())
202 }
203}
204
205impl core::ops::BitOr for Effects {
212 type Output = Self;
213
214 #[inline(always)]
215 fn bitor(self, rhs: Self) -> Self {
216 self.insert(rhs)
217 }
218}
219
220impl core::ops::BitOrAssign for Effects {
228 #[inline]
229 fn bitor_assign(&mut self, other: Self) {
230 *self = self.insert(other);
231 }
232}
233
234impl core::ops::Sub for Effects {
241 type Output = Self;
242
243 #[inline]
244 fn sub(self, other: Self) -> Self {
245 self.remove(other)
246 }
247}
248
249impl core::ops::SubAssign for Effects {
257 #[inline]
258 fn sub_assign(&mut self, other: Self) {
259 *self = self.remove(other);
260 }
261}
262
263pub(crate) struct Metadata {
264 pub(crate) name: &'static str,
265 pub(crate) escape: &'static str,
266}
267
268pub(crate) const METADATA: [Metadata; 12] = [
269 Metadata {
270 name: "BOLD",
271 escape: escape!("1"),
272 },
273 Metadata {
274 name: "DIMMED",
275 escape: escape!("2"),
276 },
277 Metadata {
278 name: "ITALIC",
279 escape: escape!("3"),
280 },
281 Metadata {
282 name: "UNDERLINE",
283 escape: escape!("4"),
284 },
285 Metadata {
286 name: "DOUBLE_UNDERLINE",
287 escape: escape!("21"),
288 },
289 Metadata {
290 name: "CURLY_UNDERLINE",
291 escape: escape!("4:3"),
292 },
293 Metadata {
294 name: "DOTTED_UNDERLINE",
295 escape: escape!("4:4"),
296 },
297 Metadata {
298 name: "DASHED_UNDERLINE",
299 escape: escape!("4:5"),
300 },
301 Metadata {
302 name: "BLINK",
303 escape: escape!("5"),
304 },
305 Metadata {
306 name: "INVERT",
307 escape: escape!("7"),
308 },
309 Metadata {
310 name: "HIDDEN",
311 escape: escape!("8"),
312 },
313 Metadata {
314 name: "STRIKETHROUGH",
315 escape: escape!("9"),
316 },
317];
318
319#[derive(Copy, Clone, Default, Debug)]
320struct EffectsDisplay(Effects);
321
322impl core::fmt::Display for EffectsDisplay {
323 #[inline]
324 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325 for index in self.0.index_iter() {
326 f.write_str(METADATA[index].escape)?;
327 }
328 Ok(())
329 }
330}
331
332#[derive(Clone, Debug, PartialEq, Eq)]
334pub struct EffectIter {
335 index: usize,
336 effects: Effects,
337}
338
339impl Iterator for EffectIter {
340 type Item = Effects;
341
342 fn next(&mut self) -> Option<Self::Item> {
343 while self.index < METADATA.len() {
344 let index = self.index;
345 self.index += 1;
346
347 let effect = Effects(1 << index);
348 if self.effects.contains(effect) {
349 return Some(effect);
350 }
351 }
352
353 None
354 }
355}
356
357#[derive(Clone, Debug, PartialEq, Eq)]
358pub(crate) struct EffectIndexIter {
359 index: usize,
360 effects: Effects,
361}
362
363impl Iterator for EffectIndexIter {
364 type Item = usize;
365
366 fn next(&mut self) -> Option<Self::Item> {
367 while self.index < METADATA.len() {
368 let index = self.index;
369 self.index += 1;
370
371 let effect = Effects(1 << index);
372 if self.effects.contains(effect) {
373 return Some(index);
374 }
375 }
376
377 None
378 }
379}
380
381#[cfg(test)]
382#[cfg(feature = "std")]
383mod test {
384 use super::*;
385
386 #[test]
387 fn print_size_of() {
388 use std::mem::size_of;
389 dbg!(size_of::<Effects>());
390 dbg!(size_of::<EffectsDisplay>());
391 }
392
393 #[test]
394 fn no_align() {
395 #[track_caller]
396 fn assert_no_align(d: impl core::fmt::Display) {
397 let expected = format!("{d}");
398 let actual = format!("{d:<10}");
399 assert_eq!(expected, actual);
400 }
401
402 assert_no_align(Effects::BOLD.render());
403 }
404}