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