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 | 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 | /// ``` |
182 | impl core::fmt::Debug for Effects { |
183 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
184 | write!(f, "Effects(" )?; |
185 | for (i, index) 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 | /// ``` |
202 | impl 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 | /// ``` |
218 | impl 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 | /// ``` |
231 | impl 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 | /// ``` |
247 | impl core::ops::SubAssign for Effects { |
248 | #[inline ] |
249 | fn sub_assign(&mut self, other: Self) { |
250 | *self = self.remove(other); |
251 | } |
252 | } |
253 | |
254 | pub(crate) struct Metadata { |
255 | pub(crate) name: &'static str, |
256 | pub(crate) escape: &'static str, |
257 | } |
258 | |
259 | pub(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)] |
311 | struct EffectsDisplay(Effects); |
312 | |
313 | impl core::fmt::Display for EffectsDisplay { |
314 | #[inline ] |
315 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
316 | for index in self.0.index_iter() { |
317 | METADATA[index].escape.fmt(f)?; |
318 | } |
319 | Ok(()) |
320 | } |
321 | } |
322 | |
323 | #[derive(Clone, Debug, PartialEq, Eq)] |
324 | pub struct EffectIter { |
325 | index: usize, |
326 | effects: Effects, |
327 | } |
328 | |
329 | impl 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 = self.index; |
335 | self.index += 1; |
336 | |
337 | let effect = 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)] |
348 | pub(crate) struct EffectIndexIter { |
349 | index: usize, |
350 | effects: Effects, |
351 | } |
352 | |
353 | impl 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 = self.index; |
359 | self.index += 1; |
360 | |
361 | let effect = Effects(1 << index); |
362 | if self.effects.contains(effect) { |
363 | return Some(index); |
364 | } |
365 | } |
366 | |
367 | None |
368 | } |
369 | } |
370 | |