1 /// A set of text effects
2 ///
3 /// # Examples
4 ///
5 /// ```rust
6 /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
7 /// ```
8 #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
9 pub struct Effects(u16);
10 
11 impl Effects {
12     /// No [`Effects`] applied
13     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     /// Not widely supported. Sometimes treated as inverse or blink
20     pub const ITALIC: Self = Effects(1 << 2);
21     /// Style extensions exist for Kitty, VTE, mintty and iTerm2.
22     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     /// Swap foreground and background colors; inconsistent emulation
34     pub const INVERT: Self = Effects(1 << 9);
35     #[allow(missing_docs)]
36     pub const HIDDEN: Self = Effects(1 << 10);
37     ///  Characters legible but marked as if for deletion. Not supported in Terminal.app
38     pub const STRIKETHROUGH: Self = Effects(1 << 11);
39 
40     /// No effects enabled
41     ///
42     /// # Examples
43     ///
44     /// ```rust
45     /// let effects = anstyle::Effects::new();
46     /// ```
47     #[inline]
new() -> Self48     pub const fn new() -> Self {
49         Self::PLAIN
50     }
51 
52     /// Check if no effects are enabled
53     ///
54     /// # Examples
55     ///
56     /// ```rust
57     /// let effects = anstyle::Effects::new();
58     /// assert!(effects.is_plain());
59     ///
60     /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
61     /// assert!(!effects.is_plain());
62     /// ```
63     #[inline]
is_plain(self) -> bool64     pub const fn is_plain(self) -> bool {
65         self.0 == Self::PLAIN.0
66     }
67 
68     /// Returns `true` if all of the effects in `other` are contained within `self`.
69     ///
70     /// # Examples
71     ///
72     /// ```rust
73     /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
74     /// assert!(effects.contains(anstyle::Effects::BOLD));
75     ///
76     /// let effects = anstyle::Effects::new();
77     /// assert!(!effects.contains(anstyle::Effects::BOLD));
78     /// ```
79     #[inline(always)]
contains(self, other: Effects) -> bool80     pub const fn contains(self, other: Effects) -> bool {
81         (other.0 & self.0) == other.0
82     }
83 
84     /// Inserts the specified effects in-place.
85     ///
86     /// # Examples
87     ///
88     /// ```rust
89     /// let effects = anstyle::Effects::new().insert(anstyle::Effects::new());
90     /// assert!(effects.is_plain());
91     ///
92     /// let effects = anstyle::Effects::new().insert(anstyle::Effects::BOLD);
93     /// assert!(effects.contains(anstyle::Effects::BOLD));
94     /// ```
95     #[inline(always)]
96     #[must_use]
insert(mut self, other: Effects) -> Self97     pub const fn insert(mut self, other: Effects) -> Self {
98         self.0 |= other.0;
99         self
100     }
101 
102     /// Removes the specified effects in-place.
103     ///
104     /// # Examples
105     ///
106     /// ```rust
107     /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).remove(anstyle::Effects::BOLD);
108     /// assert!(!effects.contains(anstyle::Effects::BOLD));
109     /// assert!(effects.contains(anstyle::Effects::UNDERLINE));
110     /// ```
111     #[inline(always)]
112     #[must_use]
remove(mut self, other: Effects) -> Self113     pub const fn remove(mut self, other: Effects) -> Self {
114         self.0 &= !other.0;
115         self
116     }
117 
118     /// Reset all effects in-place
119     /// ```rust
120     /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).clear();
121     /// assert!(!effects.contains(anstyle::Effects::BOLD));
122     /// assert!(!effects.contains(anstyle::Effects::UNDERLINE));
123     /// ```
124     #[inline(always)]
125     #[must_use]
clear(self) -> Self126     pub const fn clear(self) -> Self {
127         Self::new()
128     }
129 
130     /// Enable or disable the specified effects depending on the passed value.
131     ///
132     /// # Examples
133     ///
134     /// ```rust
135     /// let effects = anstyle::Effects::new().set(anstyle::Effects::BOLD, true);
136     /// assert!(effects.contains(anstyle::Effects::BOLD));
137     /// ```
138     #[inline]
139     #[must_use]
set(self, other: Self, enable: bool) -> Self140     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     /// Iterate over enabled effects
149     #[inline(always)]
iter(self) -> EffectIter150     pub fn iter(self) -> EffectIter {
151         EffectIter {
152             index: 0,
153             effects: self,
154         }
155     }
156 
157     /// Iterate over enabled effect indices
158     #[inline(always)]
index_iter(self) -> EffectIndexIter159     pub(crate) fn index_iter(self) -> EffectIndexIter {
160         EffectIndexIter {
161             index: 0,
162             effects: self,
163         }
164     }
165 
166     /// Render the ANSI code
167     #[inline]
render(self) -> impl core::fmt::Display + Copy168     pub fn render(self) -> impl core::fmt::Display + Copy {
169         EffectsDisplay(self)
170     }
171 
172     #[inline]
173     #[cfg(feature = "std")]
write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()>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 
182 /// # Examples
183 ///
184 /// ```rust
185 /// let effects = anstyle::Effects::new();
186 /// assert_eq!(format!("{:?}", effects), "Effects()");
187 ///
188 /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
189 /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
190 /// ```
191 impl core::fmt::Debug for Effects {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result192     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 
205 /// # Examples
206 ///
207 /// ```rust
208 /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
209 /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
210 /// ```
211 impl core::ops::BitOr for Effects {
212     type Output = Self;
213 
214     #[inline(always)]
bitor(self, rhs: Self) -> Self215     fn bitor(self, rhs: Self) -> Self {
216         self.insert(rhs)
217     }
218 }
219 
220 /// # Examples
221 ///
222 /// ```rust
223 /// let mut effects = anstyle::Effects::BOLD;
224 /// effects |= anstyle::Effects::UNDERLINE;
225 /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
226 /// ```
227 impl core::ops::BitOrAssign for Effects {
228     #[inline]
bitor_assign(&mut self, other: Self)229     fn bitor_assign(&mut self, other: Self) {
230         *self = self.insert(other);
231     }
232 }
233 
234 /// # Examples
235 ///
236 /// ```rust
237 /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE) - anstyle::Effects::BOLD;
238 /// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)");
239 /// ```
240 impl core::ops::Sub for Effects {
241     type Output = Self;
242 
243     #[inline]
sub(self, other: Self) -> Self244     fn sub(self, other: Self) -> Self {
245         self.remove(other)
246     }
247 }
248 
249 /// # Examples
250 ///
251 /// ```rust
252 /// let mut effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
253 /// effects -= anstyle::Effects::BOLD;
254 /// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)");
255 /// ```
256 impl core::ops::SubAssign for Effects {
257     #[inline]
sub_assign(&mut self, other: Self)258     fn sub_assign(&mut self, other: Self) {
259         *self = self.remove(other);
260     }
261 }
262 
263 pub(crate) struct Metadata {
264     pub(crate) name: &'static str,
265     pub(crate) escape: &'static str,
266 }
267 
268 pub(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)]
320 struct EffectsDisplay(Effects);
321 
322 impl core::fmt::Display for EffectsDisplay {
323     #[inline]
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result324     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 /// Enumerate each enabled value in [`Effects`]
333 #[derive(Clone, Debug, PartialEq, Eq)]
334 pub struct EffectIter {
335     index: usize,
336     effects: Effects,
337 }
338 
339 impl Iterator for EffectIter {
340     type Item = Effects;
341 
next(&mut self) -> Option<Self::Item>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)]
358 pub(crate) struct EffectIndexIter {
359     index: usize,
360     effects: Effects,
361 }
362 
363 impl Iterator for EffectIndexIter {
364     type Item = usize;
365 
next(&mut self) -> Option<Self::Item>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")]
383 mod test {
384     use super::*;
385 
386     #[test]
print_size_of()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]
no_align()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 }
405