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)]
9pub struct Effects(u16);
10
11impl Effects {
12 const PLAIN: Self = Effects(0);
13
14 pub const BOLD: Self = Effects(1 << 0);
15 pub const DIMMED: Self = Effects(1 << 1);
16 /// Not widely supported. Sometimes treated as inverse or blink
17 pub const ITALIC: Self = Effects(1 << 2);
18 /// Style extensions exist for Kitty, VTE, mintty and iTerm2.
19 pub const UNDERLINE: Self = Effects(1 << 3);
20 pub const DOUBLE_UNDERLINE: Self = Effects(1 << 4);
21 pub const CURLY_UNDERLINE: Self = Effects(1 << 5);
22 pub const DOTTED_UNDERLINE: Self = Effects(1 << 6);
23 pub const DASHED_UNDERLINE: Self = Effects(1 << 7);
24 pub const BLINK: Self = Effects(1 << 8);
25 /// Swap foreground and background colors; inconsistent emulation
26 pub const INVERT: Self = Effects(1 << 9);
27 pub const HIDDEN: Self = Effects(1 << 10);
28 /// Characters legible but marked as if for deletion. Not supported in Terminal.app
29 pub const STRIKETHROUGH: Self = Effects(1 << 11);
30
31 /// No effects enabled
32 ///
33 /// # Examples
34 ///
35 /// ```rust
36 /// let effects = anstyle::Effects::new();
37 /// ```
38 #[inline]
39 pub const fn new() -> Self {
40 Self::PLAIN
41 }
42
43 /// Check if no effects are enabled
44 ///
45 /// # Examples
46 ///
47 /// ```rust
48 /// let effects = anstyle::Effects::new();
49 /// assert!(effects.is_plain());
50 ///
51 /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
52 /// assert!(!effects.is_plain());
53 /// ```
54 #[inline]
55 pub const fn is_plain(self) -> bool {
56 self.0 == Self::PLAIN.0
57 }
58
59 /// Returns `true` if all of the effects in `other` are contained within `self`.
60 ///
61 /// # Examples
62 ///
63 /// ```rust
64 /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
65 /// assert!(effects.contains(anstyle::Effects::BOLD));
66 ///
67 /// let effects = anstyle::Effects::new();
68 /// assert!(!effects.contains(anstyle::Effects::BOLD));
69 /// ```
70 #[inline(always)]
71 pub const fn contains(self, other: Effects) -> bool {
72 (other.0 & self.0) == other.0
73 }
74
75 /// Inserts the specified effects in-place.
76 ///
77 /// # Examples
78 ///
79 /// ```rust
80 /// let effects = anstyle::Effects::new().insert(anstyle::Effects::new());
81 /// assert!(effects.is_plain());
82 ///
83 /// let effects = anstyle::Effects::new().insert(anstyle::Effects::BOLD);
84 /// assert!(effects.contains(anstyle::Effects::BOLD));
85 /// ```
86 #[inline(always)]
87 #[must_use]
88 pub const fn insert(mut self, other: Effects) -> Self {
89 self.0 |= other.0;
90 self
91 }
92
93 /// Removes the specified effects in-place.
94 ///
95 /// # Examples
96 ///
97 /// ```rust
98 /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).remove(anstyle::Effects::BOLD);
99 /// assert!(!effects.contains(anstyle::Effects::BOLD));
100 /// assert!(effects.contains(anstyle::Effects::UNDERLINE));
101 /// ```
102 #[inline(always)]
103 #[must_use]
104 pub const fn remove(mut self, other: Effects) -> Self {
105 self.0 &= !other.0;
106 self
107 }
108
109 /// Reset all effects in-place
110 /// ```rust
111 /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).clear();
112 /// assert!(!effects.contains(anstyle::Effects::BOLD));
113 /// assert!(!effects.contains(anstyle::Effects::UNDERLINE));
114 /// ```
115 #[inline(always)]
116 #[must_use]
117 pub const fn clear(self) -> Self {
118 Self::new()
119 }
120
121 /// Enable or disable the specified effects depending on the passed value.
122 ///
123 /// # Examples
124 ///
125 /// ```rust
126 /// let effects = anstyle::Effects::new().set(anstyle::Effects::BOLD, true);
127 /// assert!(effects.contains(anstyle::Effects::BOLD));
128 /// ```
129 #[inline]
130 #[must_use]
131 pub const fn set(self, other: Self, enable: bool) -> Self {
132 if enable {
133 self.insert(other)
134 } else {
135 self.remove(other)
136 }
137 }
138
139 /// Iterate over enabled effects
140 #[inline(always)]
141 pub fn iter(self) -> EffectIter {
142 EffectIter {
143 index: 0,
144 effects: self,
145 }
146 }
147
148 /// Iterate over enabled effect indices
149 #[inline(always)]
150 pub(crate) fn index_iter(self) -> EffectIndexIter {
151 EffectIndexIter {
152 index: 0,
153 effects: self,
154 }
155 }
156
157 /// Render the ANSI code
158 #[inline]
159 pub fn render(self) -> impl core::fmt::Display + Copy + Clone {
160 EffectsDisplay(self)
161 }
162
163 #[inline]
164 #[cfg(feature = "std")]
165 pub(crate) fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
166 for index in self.index_iter() {
167 write.write_all(METADATA[index].escape.as_bytes())?;
168 }
169 Ok(())
170 }
171}
172
173/// # Examples
174///
175/// ```rust
176/// let effects = anstyle::Effects::new();
177/// assert_eq!(format!("{:?}", effects), "Effects()");
178///
179/// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
180/// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
181/// ```
182impl core::fmt::Debug for Effects {
183 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184 write!(f, "Effects(")?;
185 for (i: usize, index: usize) in self.index_iter().enumerate() {
186 if i != 0 {
187 write!(f, " | ")?;
188 }
189 write!(f, "{}", METADATA[index].name)?;
190 }
191 write!(f, ")")?;
192 Ok(())
193 }
194}
195
196/// # Examples
197///
198/// ```rust
199/// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
200/// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
201/// ```
202impl core::ops::BitOr for Effects {
203 type Output = Self;
204
205 #[inline(always)]
206 fn bitor(self, rhs: Self) -> Self {
207 self.insert(rhs)
208 }
209}
210
211/// # Examples
212///
213/// ```rust
214/// let mut effects = anstyle::Effects::BOLD;
215/// effects |= anstyle::Effects::UNDERLINE;
216/// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)");
217/// ```
218impl core::ops::BitOrAssign for Effects {
219 #[inline]
220 fn bitor_assign(&mut self, other: Self) {
221 *self = self.insert(other);
222 }
223}
224
225/// # Examples
226///
227/// ```rust
228/// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE) - anstyle::Effects::BOLD;
229/// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)");
230/// ```
231impl core::ops::Sub for Effects {
232 type Output = Self;
233
234 #[inline]
235 fn sub(self, other: Self) -> Self {
236 self.remove(other)
237 }
238}
239
240/// # Examples
241///
242/// ```rust
243/// let mut effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE;
244/// effects -= anstyle::Effects::BOLD;
245/// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)");
246/// ```
247impl core::ops::SubAssign for Effects {
248 #[inline]
249 fn sub_assign(&mut self, other: Self) {
250 *self = self.remove(other);
251 }
252}
253
254pub(crate) struct Metadata {
255 pub(crate) name: &'static str,
256 pub(crate) escape: &'static str,
257}
258
259pub(crate) const METADATA: [Metadata; 12] = [
260 Metadata {
261 name: "BOLD",
262 escape: escape!("1"),
263 },
264 Metadata {
265 name: "DIMMED",
266 escape: escape!("2"),
267 },
268 Metadata {
269 name: "ITALIC",
270 escape: escape!("3"),
271 },
272 Metadata {
273 name: "UNDERLINE",
274 escape: escape!("4"),
275 },
276 Metadata {
277 name: "DOUBLE_UNDERLINE",
278 escape: escape!("21"),
279 },
280 Metadata {
281 name: "CURLY_UNDERLINE",
282 escape: escape!("4:3"),
283 },
284 Metadata {
285 name: "DOTTED_UNDERLINE",
286 escape: escape!("4:4"),
287 },
288 Metadata {
289 name: "DASHED_UNDERLINE",
290 escape: escape!("4:5"),
291 },
292 Metadata {
293 name: "BLINK",
294 escape: escape!("5"),
295 },
296 Metadata {
297 name: "INVERT",
298 escape: escape!("7"),
299 },
300 Metadata {
301 name: "HIDDEN",
302 escape: escape!("8"),
303 },
304 Metadata {
305 name: "STRIKETHROUGH",
306 escape: escape!("9"),
307 },
308];
309
310#[derive(Copy, Clone, Default, Debug)]
311struct EffectsDisplay(Effects);
312
313impl core::fmt::Display for EffectsDisplay {
314 #[inline]
315 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
316 for index: usize in self.0.index_iter() {
317 METADATA[index].escape.fmt(f)?;
318 }
319 Ok(())
320 }
321}
322
323#[derive(Clone, Debug, PartialEq, Eq)]
324pub struct EffectIter {
325 index: usize,
326 effects: Effects,
327}
328
329impl Iterator for EffectIter {
330 type Item = Effects;
331
332 fn next(&mut self) -> Option<Self::Item> {
333 while self.index < METADATA.len() {
334 let index: usize = self.index;
335 self.index += 1;
336
337 let effect: Effects = Effects(1 << index);
338 if self.effects.contains(effect) {
339 return Some(effect);
340 }
341 }
342
343 None
344 }
345}
346
347#[derive(Clone, Debug, PartialEq, Eq)]
348pub(crate) struct EffectIndexIter {
349 index: usize,
350 effects: Effects,
351}
352
353impl Iterator for EffectIndexIter {
354 type Item = usize;
355
356 fn next(&mut self) -> Option<Self::Item> {
357 while self.index < METADATA.len() {
358 let index: usize = self.index;
359 self.index += 1;
360
361 let effect: Effects = Effects(1 << index);
362 if self.effects.contains(effect) {
363 return Some(index);
364 }
365 }
366
367 None
368 }
369}
370