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 /// 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]
48 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]
64 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)]
80 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]
97 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]
113 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]
126 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]
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 /// Iterate over enabled effects
149 #[inline(always)]
150 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)]
159 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]
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
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/// ```
191impl core::fmt::Debug for Effects {
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 write!(f, "Effects(")?;
194 for (i: usize, index: usize) 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/// ```
211impl 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
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/// ```
227impl core::ops::BitOrAssign for Effects {
228 #[inline]
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/// ```
240impl 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
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/// ```
256impl 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: usize 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)]
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: usize = self.index;
345 self.index += 1;
346
347 let effect: Effects = 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: usize = self.index;
369 self.index += 1;
370
371 let effect: Effects = 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}
405

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more