| 1 | use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; |
|---|---|
| 2 | |
| 3 | macro_rules! auto_impl_ref_binop_trait { |
| 4 | (impl $trait_name:ident, $method:ident for $t:ty, $u:ty) => { |
| 5 | impl $trait_name<&$u> for $t { |
| 6 | type Output = <$t as $trait_name<$t>>::Output; |
| 7 | |
| 8 | #[inline] |
| 9 | fn $method(self, rhs: &$u) -> Self::Output { |
| 10 | $trait_name::$method(self, *rhs) |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | impl $trait_name<$u> for &$t { |
| 15 | type Output = <$t as $trait_name<$t>>::Output; |
| 16 | |
| 17 | #[inline] |
| 18 | fn $method(self, rhs: $u) -> Self::Output { |
| 19 | $trait_name::$method(*self, rhs) |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | impl $trait_name<&$u> for &$t { |
| 24 | type Output = <$t as $trait_name<$t>>::Output; |
| 25 | |
| 26 | #[inline] |
| 27 | fn $method(self, rhs: &$u) -> Self::Output { |
| 28 | $trait_name::$method(*self, *rhs) |
| 29 | } |
| 30 | } |
| 31 | }; |
| 32 | } |
| 33 | |
| 34 | macro_rules! impl_assign_op_trait { |
| 35 | ( |
| 36 | $trait:ident, $method:ident for $t:ty, $u:ty, using $used_trait:ident::$used_method:ident |
| 37 | ) => { |
| 38 | impl $trait<$u> for $t { |
| 39 | #[inline] |
| 40 | fn $method(&mut self, other: $u) { |
| 41 | *self = $used_trait::$used_method(&*self, other); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | impl $trait<&$u> for $t { |
| 46 | #[inline] |
| 47 | fn $method(&mut self, other: &$u) { |
| 48 | *self = $used_trait::$used_method(&*self, other); |
| 49 | } |
| 50 | } |
| 51 | }; |
| 52 | } |
| 53 | |
| 54 | const CLEARV: u8 = 0b0000_0000; |
| 55 | const BOLD: u8 = 0b0000_0001; |
| 56 | const UNDERLINE: u8 = 0b0000_0010; |
| 57 | const REVERSED: u8 = 0b0000_0100; |
| 58 | const ITALIC: u8 = 0b0000_1000; |
| 59 | const BLINK: u8 = 0b0001_0000; |
| 60 | const HIDDEN: u8 = 0b0010_0000; |
| 61 | const DIMMED: u8 = 0b0100_0000; |
| 62 | const STRIKETHROUGH: u8 = 0b1000_0000; |
| 63 | |
| 64 | static STYLES: [(u8, Styles); 8] = [ |
| 65 | (BOLD, Styles::Bold), |
| 66 | (DIMMED, Styles::Dimmed), |
| 67 | (UNDERLINE, Styles::Underline), |
| 68 | (REVERSED, Styles::Reversed), |
| 69 | (ITALIC, Styles::Italic), |
| 70 | (BLINK, Styles::Blink), |
| 71 | (HIDDEN, Styles::Hidden), |
| 72 | (STRIKETHROUGH, Styles::Strikethrough), |
| 73 | ]; |
| 74 | |
| 75 | pub static CLEAR: Style = Style(CLEARV); |
| 76 | |
| 77 | /// A combinatorial style such as bold, italics, dimmed, etc. |
| 78 | /// |
| 79 | /// ## Creation |
| 80 | /// |
| 81 | /// `Style::default()` returns a `Style` with no style switches |
| 82 | /// activated and is the default method of creating a plain `Style`. |
| 83 | /// |
| 84 | /// ## `Style` from a set of `Styles`s / `Styles` iterator |
| 85 | /// |
| 86 | /// `Style` implements `FromIter<Styles>` which means that it is |
| 87 | /// possible to do the following: |
| 88 | /// |
| 89 | /// ```rust |
| 90 | /// # use colored::*; |
| 91 | /// let style = Style::from_iter([Styles::Bold, Styles::Italic, Styles::Strikethrough]); |
| 92 | /// for styles in [Styles::Bold, Styles::Italic, Styles::Strikethrough] { |
| 93 | /// assert!(style.contains(styles)); |
| 94 | /// } |
| 95 | /// ``` |
| 96 | /// |
| 97 | /// As you can see, this is a good thing to keep in mind, although for |
| 98 | /// most cases, where you're not setting styles dynamically and are |
| 99 | /// simply creating a pre-defined set of styles, using [`Default`] and |
| 100 | /// then using the builder-style methods is likely prettier. |
| 101 | /// |
| 102 | /// ```rust |
| 103 | /// # use colored::*; |
| 104 | /// let many_styles = Style::default() |
| 105 | /// .bold() |
| 106 | /// .underline() |
| 107 | /// .italic() |
| 108 | /// .blink(); |
| 109 | /// ``` |
| 110 | /// |
| 111 | /// ## Implementation of logical bitwise operators |
| 112 | /// |
| 113 | /// `Style` implements bitwise logical operations that operate on |
| 114 | /// the held style switches collectively. By far the most common |
| 115 | /// and useful is the bitwise 'or' operator `|` which combines two |
| 116 | /// styles, merging their combined styles into one. Example: |
| 117 | /// |
| 118 | /// ```rust |
| 119 | /// # use colored::*; |
| 120 | /// let only_bold = Style::from(Styles::Bold); |
| 121 | /// // This line is actually an example of `Styles`'s bitwise logic impls but still. |
| 122 | /// let underline_and_italic = Styles::Underline | Styles::Italic; |
| 123 | /// let all_three = only_bold | underline_and_italic; |
| 124 | /// |
| 125 | /// assert!(all_three.contains(Styles::Bold) |
| 126 | /// && all_three.contains(Styles::Underline) |
| 127 | /// && all_three.contains(Styles::Italic)); |
| 128 | /// ``` |
| 129 | /// |
| 130 | /// This functionality also allows for easily turning off styles |
| 131 | /// of one `Styles` using another by combining the `&` and `!` |
| 132 | /// operators. |
| 133 | /// |
| 134 | /// ```rust |
| 135 | /// # use colored::*; |
| 136 | /// let mut very_loud_style = Style::default() |
| 137 | /// .bold() |
| 138 | /// .underline() |
| 139 | /// .italic() |
| 140 | /// .strikethrough() |
| 141 | /// .hidden(); |
| 142 | /// |
| 143 | /// // Oops! Some of those should not be in there! |
| 144 | /// // This Style now has all styles _except_ the two we don't want |
| 145 | /// // (hidden and strikethough). |
| 146 | /// let remove_mask = |
| 147 | /// !Style::from_iter([Styles::Hidden, Styles::Strikethrough]); |
| 148 | /// very_loud_style &= remove_mask; |
| 149 | /// |
| 150 | /// // `very_loud_style` no longer contains the undesired style |
| 151 | /// // switches... |
| 152 | /// assert!(!very_loud_style.contains(Styles::Hidden) |
| 153 | /// && !very_loud_style.contains(Styles::Strikethrough)); |
| 154 | /// // ...but it retains everything else! |
| 155 | /// assert!(very_loud_style.contains(Styles::Bold)); |
| 156 | /// ``` |
| 157 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 158 | pub struct Style(u8); |
| 159 | |
| 160 | /// Enum containing all of the available style settings that can be |
| 161 | /// applied to a [`Styles`] and by extension, a colrized type. |
| 162 | /// |
| 163 | /// ## Implementation of bitwise logical operators |
| 164 | /// |
| 165 | /// The implementations of [`BitAnd`], [`BitOr`], [`BitXor`], and |
| 166 | /// [`Not`] are really extensions of [`Style`]'s implementations of |
| 167 | /// the same. [`BitOr`] is great for starting chains of `Styles`'s |
| 168 | /// for creating [`Style`]'s. |
| 169 | /// |
| 170 | /// ``` |
| 171 | /// # use colored::*; |
| 172 | /// let my_styles = |
| 173 | /// // BitOr<Styles> for Styles (Styles | Styles) = Style |
| 174 | /// Styles::Bold | Styles::Underline |
| 175 | /// // BitOr<Styles> for Style (Style | Styles) = Style |
| 176 | /// | Styles::Italic; |
| 177 | /// |
| 178 | /// for s in [Styles::Bold, Styles::Underline, Styles::Italic] { |
| 179 | /// assert!(my_styles.contains(s)); |
| 180 | /// } |
| 181 | /// ``` |
| 182 | /// |
| 183 | /// [`Not`] has far fewer use cases but can still find use in |
| 184 | /// turning a `Styles` into a [`Style`] with all styles activated |
| 185 | /// except that `Styles`. |
| 186 | /// |
| 187 | /// ``` |
| 188 | /// # use colored::*; |
| 189 | /// let everything_but_bold = !Styles::Bold; |
| 190 | /// |
| 191 | /// assert!(everything_but_bold.contains(Styles::Underline)); |
| 192 | /// assert!(everything_but_bold.contains(Styles::Strikethrough)); |
| 193 | /// assert!(!everything_but_bold.contains(Styles::Bold)); |
| 194 | /// ``` |
| 195 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 196 | #[allow(missing_docs)] |
| 197 | pub enum Styles { |
| 198 | Clear, |
| 199 | Bold, |
| 200 | Dimmed, |
| 201 | Underline, |
| 202 | Reversed, |
| 203 | Italic, |
| 204 | Blink, |
| 205 | Hidden, |
| 206 | Strikethrough, |
| 207 | } |
| 208 | |
| 209 | impl Styles { |
| 210 | fn to_str<'a>(self) -> &'a str { |
| 211 | match self { |
| 212 | Styles::Clear => "", // unreachable, but we don't want to panic |
| 213 | Styles::Bold => "1", |
| 214 | Styles::Dimmed => "2", |
| 215 | Styles::Italic => "3", |
| 216 | Styles::Underline => "4", |
| 217 | Styles::Blink => "5", |
| 218 | Styles::Reversed => "7", |
| 219 | Styles::Hidden => "8", |
| 220 | Styles::Strikethrough => "9", |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | fn to_u8(self) -> u8 { |
| 225 | match self { |
| 226 | Styles::Clear => CLEARV, |
| 227 | Styles::Bold => BOLD, |
| 228 | Styles::Dimmed => DIMMED, |
| 229 | Styles::Italic => ITALIC, |
| 230 | Styles::Underline => UNDERLINE, |
| 231 | Styles::Blink => BLINK, |
| 232 | Styles::Reversed => REVERSED, |
| 233 | Styles::Hidden => HIDDEN, |
| 234 | Styles::Strikethrough => STRIKETHROUGH, |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | fn from_u8(u: u8) -> Option<Vec<Styles>> { |
| 239 | if u == CLEARV { |
| 240 | return None; |
| 241 | } |
| 242 | |
| 243 | let res: Vec<Styles> = STYLES |
| 244 | .iter() |
| 245 | .filter(|&(mask, _)| (0 != (u & mask))) |
| 246 | .map(|&(_, value)| value) |
| 247 | .collect(); |
| 248 | if res.is_empty() { |
| 249 | None |
| 250 | } else { |
| 251 | Some(res) |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | impl BitAnd<Styles> for Styles { |
| 257 | type Output = Style; |
| 258 | |
| 259 | fn bitand(self, rhs: Styles) -> Self::Output { |
| 260 | Style(self.to_u8() & rhs.to_u8()) |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Styles); |
| 265 | |
| 266 | impl BitAnd<Style> for Styles { |
| 267 | type Output = Style; |
| 268 | |
| 269 | fn bitand(self, rhs: Style) -> Self::Output { |
| 270 | Style(self.to_u8() & rhs.0) |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Style); |
| 275 | |
| 276 | impl BitOr<Styles> for Styles { |
| 277 | type Output = Style; |
| 278 | |
| 279 | fn bitor(self, rhs: Styles) -> Self::Output { |
| 280 | Style(self.to_u8() | rhs.to_u8()) |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Styles); |
| 285 | |
| 286 | impl BitOr<Style> for Styles { |
| 287 | type Output = Style; |
| 288 | |
| 289 | fn bitor(self, rhs: Style) -> Self::Output { |
| 290 | Style(self.to_u8() | rhs.0) |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Style); |
| 295 | |
| 296 | impl BitXor<Styles> for Styles { |
| 297 | type Output = Style; |
| 298 | |
| 299 | fn bitxor(self, rhs: Styles) -> Self::Output { |
| 300 | Style(self.to_u8() ^ rhs.to_u8()) |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Styles); |
| 305 | |
| 306 | impl BitXor<Style> for Styles { |
| 307 | type Output = Style; |
| 308 | |
| 309 | fn bitxor(self, rhs: Style) -> Self::Output { |
| 310 | Style(self.to_u8() ^ rhs.0) |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Style); |
| 315 | |
| 316 | impl Not for Styles { |
| 317 | type Output = Style; |
| 318 | |
| 319 | fn not(self) -> Self::Output { |
| 320 | Style(!self.to_u8()) |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | impl Not for &Styles { |
| 325 | type Output = Style; |
| 326 | |
| 327 | fn not(self) -> Self::Output { |
| 328 | Style(!self.to_u8()) |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | impl Style { |
| 333 | /// Check if the current style has one of [`Styles`](Styles) switched on. |
| 334 | /// |
| 335 | /// ```rust |
| 336 | /// # use colored::*; |
| 337 | /// let colored = "".bold().italic(); |
| 338 | /// assert_eq!(colored.style.contains(Styles::Bold), true); |
| 339 | /// assert_eq!(colored.style.contains(Styles::Italic), true); |
| 340 | /// assert_eq!(colored.style.contains(Styles::Dimmed), false); |
| 341 | /// ``` |
| 342 | pub fn contains(self, style: Styles) -> bool { |
| 343 | let s = style.to_u8(); |
| 344 | self.0 & s == s |
| 345 | } |
| 346 | |
| 347 | pub(crate) fn to_str(self) -> String { |
| 348 | let styles = Styles::from_u8(self.0).unwrap_or_default(); |
| 349 | styles |
| 350 | .iter() |
| 351 | .map(|s| s.to_str()) |
| 352 | .collect::<Vec<&str>>() |
| 353 | .join(";") |
| 354 | } |
| 355 | |
| 356 | /// Adds the `two` style switch to this Style. |
| 357 | /// |
| 358 | /// ```rust |
| 359 | /// # use colored::*; |
| 360 | /// let cstr = "".red().bold(); |
| 361 | /// let mut style = cstr.style; |
| 362 | /// style.add(Styles::Italic); |
| 363 | /// let mut cstr2 = "".blue(); |
| 364 | /// cstr2.style = style; |
| 365 | /// |
| 366 | /// assert!(cstr2.style.contains(Styles::Bold)); |
| 367 | /// assert!(cstr2.style.contains(Styles::Italic)); |
| 368 | /// assert_eq!(cstr2.fgcolor, Some(Color::Blue)); |
| 369 | /// ``` |
| 370 | pub fn add(&mut self, two: Styles) { |
| 371 | self.0 |= two.to_u8(); |
| 372 | } |
| 373 | |
| 374 | /// Turns off a style switch. |
| 375 | /// |
| 376 | /// ```rust |
| 377 | /// use colored::*; |
| 378 | /// let cstr = "".red().bold().italic(); |
| 379 | /// let mut style = cstr.style; |
| 380 | /// style.remove(Styles::Italic); |
| 381 | /// let mut cstr2 = "".blue(); |
| 382 | /// cstr2.style = style; |
| 383 | /// assert!(cstr2.style.contains(Styles::Bold)); |
| 384 | /// assert!(!cstr2.style.contains(Styles::Italic)); |
| 385 | /// assert_eq!(cstr2.fgcolor, Some(Color::Blue)); |
| 386 | /// ``` |
| 387 | pub fn remove(&mut self, two: Styles) { |
| 388 | self.0 &= !two.to_u8(); |
| 389 | } |
| 390 | |
| 391 | /// Makes this `Style` include Bold. |
| 392 | pub fn bold(mut self) -> Self { |
| 393 | self.add(Styles::Bold); |
| 394 | self |
| 395 | } |
| 396 | |
| 397 | /// Makes this `Style` include Dimmed. |
| 398 | pub fn dimmed(mut self) -> Self { |
| 399 | self.add(Styles::Dimmed); |
| 400 | self |
| 401 | } |
| 402 | |
| 403 | /// Makes this `Style` include Underline. |
| 404 | pub fn underline(mut self) -> Self { |
| 405 | self.add(Styles::Underline); |
| 406 | self |
| 407 | } |
| 408 | |
| 409 | /// Makes this `Style` include Reversed. |
| 410 | pub fn reversed(mut self) -> Self { |
| 411 | self.add(Styles::Reversed); |
| 412 | self |
| 413 | } |
| 414 | |
| 415 | /// Makes this `Style` include Italic. |
| 416 | pub fn italic(mut self) -> Self { |
| 417 | self.add(Styles::Italic); |
| 418 | self |
| 419 | } |
| 420 | |
| 421 | /// Makes this `Style` include Blink. |
| 422 | pub fn blink(mut self) -> Self { |
| 423 | self.add(Styles::Blink); |
| 424 | self |
| 425 | } |
| 426 | |
| 427 | /// Makes this `Style` include Hidden. |
| 428 | pub fn hidden(mut self) -> Self { |
| 429 | self.add(Styles::Hidden); |
| 430 | self |
| 431 | } |
| 432 | |
| 433 | /// Makes this `Style` include Strikethrough. |
| 434 | pub fn strikethrough(mut self) -> Self { |
| 435 | self.add(Styles::Strikethrough); |
| 436 | self |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | impl BitAnd<Style> for Style { |
| 441 | type Output = Style; |
| 442 | |
| 443 | fn bitand(self, rhs: Style) -> Self::Output { |
| 444 | Style(self.0 & rhs.0) |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Style); |
| 449 | |
| 450 | impl BitAnd<Styles> for Style { |
| 451 | type Output = Style; |
| 452 | |
| 453 | fn bitand(self, rhs: Styles) -> Self::Output { |
| 454 | Style(self.0 & rhs.to_u8()) |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Styles); |
| 459 | |
| 460 | impl BitOr<Style> for Style { |
| 461 | type Output = Style; |
| 462 | |
| 463 | fn bitor(self, rhs: Style) -> Self::Output { |
| 464 | Style(self.0 | rhs.0) |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Style); |
| 469 | |
| 470 | impl BitOr<Styles> for Style { |
| 471 | type Output = Style; |
| 472 | |
| 473 | fn bitor(self, rhs: Styles) -> Self::Output { |
| 474 | Style(self.0 | rhs.to_u8()) |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Styles); |
| 479 | |
| 480 | impl BitXor<Style> for Style { |
| 481 | type Output = Style; |
| 482 | |
| 483 | fn bitxor(self, rhs: Style) -> Self::Output { |
| 484 | Style(self.0 ^ rhs.0) |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Style); |
| 489 | |
| 490 | impl BitXor<Styles> for Style { |
| 491 | type Output = Style; |
| 492 | |
| 493 | fn bitxor(self, rhs: Styles) -> Self::Output { |
| 494 | Style(self.0 ^ rhs.to_u8()) |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Styles); |
| 499 | |
| 500 | impl Not for Style { |
| 501 | type Output = Style; |
| 502 | |
| 503 | fn not(self) -> Self::Output { |
| 504 | Style(!self.0) |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | impl Not for &Style { |
| 509 | type Output = Style; |
| 510 | |
| 511 | fn not(self) -> Self::Output { |
| 512 | Style(!self.0) |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Style, using BitAnd::bitand); |
| 517 | |
| 518 | impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Styles, using BitAnd::bitand); |
| 519 | |
| 520 | impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Style, using BitOr::bitor); |
| 521 | |
| 522 | impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Styles, using BitOr::bitor); |
| 523 | |
| 524 | impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Style, using BitXor::bitxor); |
| 525 | |
| 526 | impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Styles, using BitXor::bitxor); |
| 527 | |
| 528 | impl Default for Style { |
| 529 | fn default() -> Self { |
| 530 | CLEAR |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | impl From<Styles> for Style { |
| 535 | fn from(value: Styles) -> Self { |
| 536 | Style(value.to_u8()) |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | impl From<&Styles> for Style { |
| 541 | fn from(value: &Styles) -> Self { |
| 542 | Style(value.to_u8()) |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | impl FromIterator<Styles> for Style { |
| 547 | fn from_iter<T: IntoIterator<Item = Styles>>(iter: T) -> Self { |
| 548 | let mut style: Style = Style::default(); |
| 549 | for styles: Styles in iter.into_iter() { |
| 550 | style.add(two:styles); |
| 551 | } |
| 552 | style |
| 553 | } |
| 554 | } |
| 555 | |
| 556 | #[cfg(test)] |
| 557 | mod tests { |
| 558 | use super::*; |
| 559 | |
| 560 | mod u8_to_styles_invalid_is_none { |
| 561 | use super::super::Styles; |
| 562 | use super::super::CLEARV; |
| 563 | |
| 564 | #[test] |
| 565 | fn empty_is_none() { |
| 566 | assert_eq!(None, Styles::from_u8(CLEARV)); |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | mod u8_to_styles_isomorphism { |
| 571 | use super::super::Styles; |
| 572 | use super::super::{ |
| 573 | BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE, |
| 574 | }; |
| 575 | |
| 576 | macro_rules! value_isomorph { |
| 577 | ($name:ident, $value:expr) => { |
| 578 | #[test] |
| 579 | fn $name() { |
| 580 | let u = Styles::from_u8($value); |
| 581 | assert!( |
| 582 | u.is_some(), |
| 583 | "{}: Styles::from_u8 -> None", |
| 584 | stringify!($value) |
| 585 | ); |
| 586 | let u = u.unwrap(); |
| 587 | assert!( |
| 588 | u.len() == 1, |
| 589 | "{}: Styles::from_u8 found {} styles (expected 1)", |
| 590 | stringify!($value), |
| 591 | u.len() |
| 592 | ); |
| 593 | assert!( |
| 594 | u[0].to_u8() == $value, |
| 595 | "{}: to_u8() doesn't match its const value", |
| 596 | stringify!($value) |
| 597 | ); |
| 598 | } |
| 599 | }; |
| 600 | } |
| 601 | |
| 602 | value_isomorph!(bold, BOLD); |
| 603 | value_isomorph!(underline, UNDERLINE); |
| 604 | value_isomorph!(reversed, REVERSED); |
| 605 | value_isomorph!(italic, ITALIC); |
| 606 | value_isomorph!(blink, BLINK); |
| 607 | value_isomorph!(hidden, HIDDEN); |
| 608 | value_isomorph!(dimmed, DIMMED); |
| 609 | value_isomorph!(strikethrough, STRIKETHROUGH); |
| 610 | } |
| 611 | |
| 612 | mod styles_combine_complex { |
| 613 | use super::super::Styles::*; |
| 614 | use super::super::{Style, Styles}; |
| 615 | |
| 616 | fn style_from_multiples(styles: &[Styles]) -> Style { |
| 617 | let mut res = Style(styles[0].to_u8()); |
| 618 | for s in &styles[1..] { |
| 619 | res = Style(res.0 | s.to_u8()); |
| 620 | } |
| 621 | res |
| 622 | } |
| 623 | |
| 624 | macro_rules! test_aggreg { |
| 625 | ($styles:expr, $expect:expr) => {{ |
| 626 | let v = style_from_multiples($styles); |
| 627 | let r = Styles::from_u8(v.0).expect("should find styles"); |
| 628 | assert_eq!(&$expect as &[Styles], &r[..]) |
| 629 | }}; |
| 630 | } |
| 631 | |
| 632 | #[test] |
| 633 | fn aggreg1() { |
| 634 | let styles: &[Styles] = &[Bold, Bold, Bold]; |
| 635 | test_aggreg!(styles, [Bold]); |
| 636 | } |
| 637 | |
| 638 | #[test] |
| 639 | fn aggreg2() { |
| 640 | let styles: &[Styles] = &[Italic, Italic, Bold, Bold]; |
| 641 | test_aggreg!(styles, [Bold, Italic]); |
| 642 | } |
| 643 | |
| 644 | #[test] |
| 645 | fn aggreg3() { |
| 646 | let styles: &[Styles] = &[Bold, Italic, Bold]; |
| 647 | test_aggreg!(styles, [Bold, Italic]); |
| 648 | } |
| 649 | |
| 650 | macro_rules! test_combine { |
| 651 | ($styles:expr) => {{ |
| 652 | let v = style_from_multiples($styles); |
| 653 | let r = Styles::from_u8(v.0).expect("should find styles"); |
| 654 | assert_eq!($styles, &r[..]) |
| 655 | }}; |
| 656 | } |
| 657 | |
| 658 | #[test] |
| 659 | fn two1() { |
| 660 | let s: &[Styles] = &[Bold, Underline]; |
| 661 | test_combine!(s); |
| 662 | } |
| 663 | |
| 664 | #[test] |
| 665 | fn two2() { |
| 666 | let s: &[Styles] = &[Underline, Italic]; |
| 667 | test_combine!(s); |
| 668 | } |
| 669 | |
| 670 | #[test] |
| 671 | fn two3() { |
| 672 | let s: &[Styles] = &[Bold, Italic]; |
| 673 | test_combine!(s); |
| 674 | } |
| 675 | |
| 676 | #[test] |
| 677 | fn three1() { |
| 678 | let s: &[Styles] = &[Bold, Underline, Italic]; |
| 679 | test_combine!(s); |
| 680 | } |
| 681 | |
| 682 | #[test] |
| 683 | fn three2() { |
| 684 | let s: &[Styles] = &[Dimmed, Underline, Italic]; |
| 685 | test_combine!(s); |
| 686 | } |
| 687 | |
| 688 | #[test] |
| 689 | fn four() { |
| 690 | let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden]; |
| 691 | test_combine!(s); |
| 692 | } |
| 693 | |
| 694 | #[test] |
| 695 | fn five() { |
| 696 | let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden]; |
| 697 | test_combine!(s); |
| 698 | } |
| 699 | |
| 700 | #[test] |
| 701 | fn six() { |
| 702 | let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden]; |
| 703 | test_combine!(s); |
| 704 | } |
| 705 | |
| 706 | #[test] |
| 707 | fn all() { |
| 708 | let s: &[Styles] = &[ |
| 709 | Bold, |
| 710 | Dimmed, |
| 711 | Underline, |
| 712 | Reversed, |
| 713 | Italic, |
| 714 | Blink, |
| 715 | Hidden, |
| 716 | Strikethrough, |
| 717 | ]; |
| 718 | test_combine!(s); |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | #[test] |
| 723 | fn test_style_contains() { |
| 724 | let mut style = Style(Styles::Bold.to_u8()); |
| 725 | style.add(Styles::Italic); |
| 726 | |
| 727 | assert!(style.contains(Styles::Bold)); |
| 728 | assert!(style.contains(Styles::Italic)); |
| 729 | assert!(!style.contains(Styles::Dimmed)); |
| 730 | } |
| 731 | |
| 732 | mod style_bitwise_logic { |
| 733 | use super::*; |
| 734 | |
| 735 | macro_rules! check_impl { |
| 736 | ($lh:expr, $method:path, $rh:expr => $res:expr) => { |
| 737 | assert_eq!($method($lh, $rh), $res); |
| 738 | assert_eq!($method(&$lh, $rh), $res); |
| 739 | assert_eq!($method($lh, &$rh), $res); |
| 740 | assert_eq!($method(&$lh, &$rh), $res); |
| 741 | }; |
| 742 | } |
| 743 | |
| 744 | macro_rules! check_impl_reflexive { |
| 745 | ($lh:expr, $method:path, $rh:expr => $res:expr) => { |
| 746 | check_impl!($lh, $method, $rh => $res); |
| 747 | check_impl!($rh, $method, $lh => $res); |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | /// TTABLE = TRUTH_TABLE |
| 752 | const TTABLE: (u8, u8) = (0b0101, 0b0011); |
| 753 | |
| 754 | #[test] |
| 755 | fn binops() { |
| 756 | let tstyle_l = Style(TTABLE.0); |
| 757 | let tstyle_r = Style(TTABLE.1); |
| 758 | let and_res = Style(TTABLE.0 & TTABLE.1); |
| 759 | let or_res = Style(TTABLE.0 | TTABLE.1); |
| 760 | let xor_res = Style(TTABLE.0 ^ TTABLE.1); |
| 761 | |
| 762 | check_impl!(tstyle_l, BitAnd::bitand, tstyle_r => and_res); |
| 763 | check_impl!(tstyle_l, BitOr::bitor, tstyle_r => or_res); |
| 764 | check_impl!(tstyle_l, BitXor::bitxor, tstyle_r => xor_res); |
| 765 | } |
| 766 | |
| 767 | #[test] |
| 768 | fn binops_with_styles() { |
| 769 | let bold_underline = Style(0b0011); |
| 770 | |
| 771 | check_impl_reflexive!( |
| 772 | bold_underline, |
| 773 | BitAnd::bitand, |
| 774 | Styles::Bold |
| 775 | => Style(0b0000_0001) |
| 776 | ); |
| 777 | check_impl_reflexive!( |
| 778 | bold_underline, |
| 779 | BitOr::bitor, |
| 780 | Styles::Reversed |
| 781 | => Style(0b0000_0111) |
| 782 | ); |
| 783 | check_impl_reflexive!( |
| 784 | bold_underline, |
| 785 | BitXor::bitxor, |
| 786 | Styles::Underline |
| 787 | => Style(0b0000_0001) |
| 788 | ); |
| 789 | } |
| 790 | |
| 791 | #[test] |
| 792 | fn not() { |
| 793 | let not_bold = !Style(BOLD); |
| 794 | assert!(!not_bold.contains(Styles::Bold)); |
| 795 | assert!(not_bold.contains(Styles::Strikethrough)); |
| 796 | assert_eq!(!Style(0b0011_0101), Style(0b1100_1010)); |
| 797 | } |
| 798 | |
| 799 | #[test] |
| 800 | fn assign_ops() { |
| 801 | let original_style = Style(0b0011); |
| 802 | let op_style = Style(0b0101); |
| 803 | |
| 804 | let mut style = original_style; |
| 805 | style &= op_style; |
| 806 | assert_eq!(style, Style(0b0001)); |
| 807 | |
| 808 | style = original_style; |
| 809 | style |= op_style; |
| 810 | assert_eq!(style, Style(0b0111)); |
| 811 | |
| 812 | style = original_style; |
| 813 | style ^= op_style; |
| 814 | assert_eq!(style, Style(0b0110)); |
| 815 | } |
| 816 | |
| 817 | #[test] |
| 818 | fn assign_ops_with_styles() { |
| 819 | let original_style = Style(0b0011); |
| 820 | |
| 821 | let mut style = original_style; |
| 822 | style &= Styles::Bold; |
| 823 | assert_eq!(style, Style(0b0001)); |
| 824 | |
| 825 | style = original_style; |
| 826 | style |= Styles::Reversed; |
| 827 | assert_eq!(style, Style(0b0111)); |
| 828 | |
| 829 | style = original_style; |
| 830 | style ^= Styles::Bold; |
| 831 | assert_eq!(style, Style(0b0010)); |
| 832 | } |
| 833 | |
| 834 | #[test] |
| 835 | fn styles_binops() { |
| 836 | check_impl!( |
| 837 | Styles::Bold, |
| 838 | BitAnd::bitand, |
| 839 | Styles::Bold |
| 840 | => Style(0b0000_0001) |
| 841 | ); |
| 842 | // The check_impl is only to verify it works with all the combos |
| 843 | // of refs. We already know it compines so let's spare ourselves |
| 844 | // the extra assertions. |
| 845 | assert_eq!(Styles::Bold & Styles::Underline, Style(0b0000_0000)); |
| 846 | |
| 847 | check_impl!( |
| 848 | Styles::Bold, |
| 849 | BitOr::bitor, |
| 850 | Styles::Underline |
| 851 | => Style(0b0000_0011) |
| 852 | ); |
| 853 | assert_eq!(Styles::Bold | Styles::Bold, Style(0b0000_0001)); |
| 854 | |
| 855 | check_impl!( |
| 856 | Styles::Bold, |
| 857 | BitXor::bitxor, |
| 858 | Styles::Underline |
| 859 | => Style(0b0000_0011) |
| 860 | ); |
| 861 | assert_eq!(Styles::Bold ^ Styles::Bold, Style(0b0000_0000)); |
| 862 | } |
| 863 | |
| 864 | #[test] |
| 865 | fn styles_not() { |
| 866 | let not_bold = !Styles::Bold; |
| 867 | assert_eq!(not_bold, Style(!BOLD)); |
| 868 | } |
| 869 | } |
| 870 | } |
| 871 |
Definitions
- auto_impl_ref_binop_trait
- impl_assign_op_trait
- Style
- Styles
- Clear
- Bold
- Dimmed
- Underline
- Reversed
- Italic
- Blink
- Hidden
- Strikethrough
- to_str
- to_u8
- from_u8
- Output
- bitand
- bitand
- Output
- bitand
- bitand
- Output
- bitor
- bitor
- Output
- bitor
- bitor
- Output
- bitxor
- bitxor
- Output
- bitxor
- bitxor
- Output
- not
- Output
- not
- contains
- to_str
- add
- remove
- bold
- dimmed
- underline
- reversed
- italic
- blink
- hidden
- strikethrough
- Output
- bitand
- bitand
- Output
- bitand
- bitand
- Output
- bitor
- bitor
- Output
- bitor
- bitor
- Output
- bitxor
- bitxor
- Output
- bitxor
- bitxor
- Output
- not
- Output
- not
- bitand_assign
- bitand_assign
- bitor_assign
- bitor_assign
- bitxor_assign
- bitxor_assign
- default
- from
- from
Learn Rust with the experts
Find out more
