1use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
2
3macro_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
34macro_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
54const CLEARV: u8 = 0b0000_0000;
55const BOLD: u8 = 0b0000_0001;
56const UNDERLINE: u8 = 0b0000_0010;
57const REVERSED: u8 = 0b0000_0100;
58const ITALIC: u8 = 0b0000_1000;
59const BLINK: u8 = 0b0001_0000;
60const HIDDEN: u8 = 0b0010_0000;
61const DIMMED: u8 = 0b0100_0000;
62const STRIKETHROUGH: u8 = 0b1000_0000;
63
64static 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
75pub static CLEAR: Style = Style(CLEARV);
76
77#[derive(Clone, Copy, PartialEq, Eq, Debug)]
158pub struct Style(u8);
159
160#[derive(Clone, Copy, PartialEq, Eq, Debug)]
196#[allow(missing_docs)]
197pub enum Styles {
198 Clear,
199 Bold,
200 Dimmed,
201 Underline,
202 Reversed,
203 Italic,
204 Blink,
205 Hidden,
206 Strikethrough,
207}
208
209impl Styles {
210 const fn to_str<'a>(self) -> &'a str {
211 match self {
212 Self::Clear => "", Self::Bold => "1",
214 Self::Dimmed => "2",
215 Self::Italic => "3",
216 Self::Underline => "4",
217 Self::Blink => "5",
218 Self::Reversed => "7",
219 Self::Hidden => "8",
220 Self::Strikethrough => "9",
221 }
222 }
223
224 const fn to_u8(self) -> u8 {
225 match self {
226 Self::Clear => CLEARV,
227 Self::Bold => BOLD,
228 Self::Dimmed => DIMMED,
229 Self::Italic => ITALIC,
230 Self::Underline => UNDERLINE,
231 Self::Blink => BLINK,
232 Self::Reversed => REVERSED,
233 Self::Hidden => HIDDEN,
234 Self::Strikethrough => STRIKETHROUGH,
235 }
236 }
237
238 fn from_u8(u: u8) -> Option<Vec<Self>> {
239 if u == CLEARV {
240 return None;
241 }
242
243 let res: Vec<Self> = STYLES
244 .iter()
245 .filter(|&(mask, _)| 0 != (u & mask))
246 .map(|&(_, value)| value)
247 .collect();
248
249 if res.is_empty() {
250 None
251 } else {
252 Some(res)
253 }
254 }
255}
256
257impl BitAnd<Self> for Styles {
258 type Output = Style;
259
260 fn bitand(self, rhs: Self) -> Self::Output {
261 Style(self.to_u8() & rhs.to_u8())
262 }
263}
264
265auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Styles);
266
267impl BitAnd<Style> for Styles {
268 type Output = Style;
269
270 fn bitand(self, rhs: Style) -> Self::Output {
271 Style(self.to_u8() & rhs.0)
272 }
273}
274
275auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Style);
276
277impl BitOr<Self> for Styles {
278 type Output = Style;
279
280 fn bitor(self, rhs: Self) -> Self::Output {
281 Style(self.to_u8() | rhs.to_u8())
282 }
283}
284
285auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Styles);
286
287impl BitOr<Style> for Styles {
288 type Output = Style;
289
290 fn bitor(self, rhs: Style) -> Self::Output {
291 Style(self.to_u8() | rhs.0)
292 }
293}
294
295auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Style);
296
297impl BitXor<Self> for Styles {
298 type Output = Style;
299
300 fn bitxor(self, rhs: Self) -> Self::Output {
301 Style(self.to_u8() ^ rhs.to_u8())
302 }
303}
304
305auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Styles);
306
307impl BitXor<Style> for Styles {
308 type Output = Style;
309
310 fn bitxor(self, rhs: Style) -> Self::Output {
311 Style(self.to_u8() ^ rhs.0)
312 }
313}
314
315auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Style);
316
317impl Not for Styles {
318 type Output = Style;
319
320 fn not(self) -> Self::Output {
321 Style(!self.to_u8())
322 }
323}
324
325impl Not for &Styles {
326 type Output = Style;
327
328 fn not(self) -> Self::Output {
329 Style(!self.to_u8())
330 }
331}
332
333impl Style {
334 #[must_use]
344 pub fn contains(self, style: Styles) -> bool {
345 let s = style.to_u8();
346 self.0 & s == s
347 }
348
349 pub(crate) fn to_str(self) -> String {
350 let styles = Styles::from_u8(self.0).unwrap_or_default();
351 styles
352 .iter()
353 .map(|s| s.to_str())
354 .collect::<Vec<&str>>()
355 .join(";")
356 }
357
358 pub fn add(&mut self, two: Styles) {
373 self.0 |= two.to_u8();
374 }
375
376 pub fn remove(&mut self, two: Styles) {
390 self.0 &= !two.to_u8();
391 }
392
393 #[must_use]
395 pub fn bold(mut self) -> Self {
396 self.add(Styles::Bold);
397 self
398 }
399
400 #[must_use]
402 pub fn dimmed(mut self) -> Self {
403 self.add(Styles::Dimmed);
404 self
405 }
406
407 #[must_use]
409 pub fn underline(mut self) -> Self {
410 self.add(Styles::Underline);
411 self
412 }
413
414 #[must_use]
416 pub fn reversed(mut self) -> Self {
417 self.add(Styles::Reversed);
418 self
419 }
420
421 #[must_use]
423 pub fn italic(mut self) -> Self {
424 self.add(Styles::Italic);
425 self
426 }
427
428 #[must_use]
430 pub fn blink(mut self) -> Self {
431 self.add(Styles::Blink);
432 self
433 }
434
435 #[must_use]
437 pub fn hidden(mut self) -> Self {
438 self.add(Styles::Hidden);
439 self
440 }
441
442 #[must_use]
444 pub fn strikethrough(mut self) -> Self {
445 self.add(Styles::Strikethrough);
446 self
447 }
448}
449
450impl BitAnd<Self> for Style {
451 type Output = Self;
452
453 fn bitand(self, rhs: Self) -> Self::Output {
454 Self(self.0 & rhs.0)
455 }
456}
457
458auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Style);
459
460impl BitAnd<Styles> for Style {
461 type Output = Self;
462
463 fn bitand(self, rhs: Styles) -> Self::Output {
464 Self(self.0 & rhs.to_u8())
465 }
466}
467
468auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Styles);
469
470impl BitOr<Self> for Style {
471 type Output = Self;
472
473 fn bitor(self, rhs: Self) -> Self::Output {
474 Self(self.0 | rhs.0)
475 }
476}
477
478auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Style);
479
480impl BitOr<Styles> for Style {
481 type Output = Self;
482
483 fn bitor(self, rhs: Styles) -> Self::Output {
484 Self(self.0 | rhs.to_u8())
485 }
486}
487
488auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Styles);
489
490impl BitXor<Self> for Style {
491 type Output = Self;
492
493 fn bitxor(self, rhs: Self) -> Self::Output {
494 Self(self.0 ^ rhs.0)
495 }
496}
497
498auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Style);
499
500impl BitXor<Styles> for Style {
501 type Output = Self;
502
503 fn bitxor(self, rhs: Styles) -> Self::Output {
504 Self(self.0 ^ rhs.to_u8())
505 }
506}
507
508auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Styles);
509
510impl Not for Style {
511 type Output = Self;
512
513 fn not(self) -> Self::Output {
514 Self(!self.0)
515 }
516}
517
518impl Not for &Style {
519 type Output = Style;
520
521 fn not(self) -> Self::Output {
522 Style(!self.0)
523 }
524}
525
526impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Style, using BitAnd::bitand);
527
528impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Styles, using BitAnd::bitand);
529
530impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Style, using BitOr::bitor);
531
532impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Styles, using BitOr::bitor);
533
534impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Style, using BitXor::bitxor);
535
536impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Styles, using BitXor::bitxor);
537
538impl Default for Style {
539 fn default() -> Self {
540 CLEAR
541 }
542}
543
544impl From<Styles> for Style {
545 fn from(value: Styles) -> Self {
546 Self(value.to_u8())
547 }
548}
549
550impl From<&Styles> for Style {
551 fn from(value: &Styles) -> Self {
552 Self(value.to_u8())
553 }
554}
555
556impl FromIterator<Styles> for Style {
557 fn from_iter<T: IntoIterator<Item = Styles>>(iter: T) -> Self {
558 let mut style = Self::default();
559 for styles in iter {
560 style.add(styles);
561 }
562 style
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 use super::*;
569
570 mod u8_to_styles_invalid_is_none {
571 use super::super::Styles;
572 use super::super::CLEARV;
573
574 #[test]
575 fn empty_is_none() {
576 assert_eq!(None, Styles::from_u8(CLEARV));
577 }
578 }
579
580 mod u8_to_styles_isomorphism {
581 use super::super::Styles;
582 use super::super::{
583 BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
584 };
585
586 macro_rules! value_isomorph {
587 ($name:ident, $value:expr) => {
588 #[test]
589 fn $name() {
590 let u = Styles::from_u8($value);
591 assert!(
592 u.is_some(),
593 "{}: Styles::from_u8 -> None",
594 stringify!($value)
595 );
596 let u = u.unwrap();
597 assert!(
598 u.len() == 1,
599 "{}: Styles::from_u8 found {} styles (expected 1)",
600 stringify!($value),
601 u.len()
602 );
603 assert!(
604 u[0].to_u8() == $value,
605 "{}: to_u8() doesn't match its const value",
606 stringify!($value)
607 );
608 }
609 };
610 }
611
612 value_isomorph!(bold, BOLD);
613 value_isomorph!(underline, UNDERLINE);
614 value_isomorph!(reversed, REVERSED);
615 value_isomorph!(italic, ITALIC);
616 value_isomorph!(blink, BLINK);
617 value_isomorph!(hidden, HIDDEN);
618 value_isomorph!(dimmed, DIMMED);
619 value_isomorph!(strikethrough, STRIKETHROUGH);
620 }
621
622 mod styles_combine_complex {
623 use super::super::Styles::*;
624 use super::super::{Style, Styles};
625
626 fn style_from_multiples(styles: &[Styles]) -> Style {
627 let mut res = Style(styles[0].to_u8());
628 for s in &styles[1..] {
629 res = Style(res.0 | s.to_u8());
630 }
631 res
632 }
633
634 macro_rules! test_aggreg {
635 ($styles:expr, $expect:expr) => {{
636 let v = style_from_multiples($styles);
637 let r = Styles::from_u8(v.0).expect("should find styles");
638 assert_eq!(&$expect as &[Styles], &r[..])
639 }};
640 }
641
642 #[test]
643 fn aggreg1() {
644 let styles: &[Styles] = &[Bold, Bold, Bold];
645 test_aggreg!(styles, [Bold]);
646 }
647
648 #[test]
649 fn aggreg2() {
650 let styles: &[Styles] = &[Italic, Italic, Bold, Bold];
651 test_aggreg!(styles, [Bold, Italic]);
652 }
653
654 #[test]
655 fn aggreg3() {
656 let styles: &[Styles] = &[Bold, Italic, Bold];
657 test_aggreg!(styles, [Bold, Italic]);
658 }
659
660 macro_rules! test_combine {
661 ($styles:expr) => {{
662 let v = style_from_multiples($styles);
663 let r = Styles::from_u8(v.0).expect("should find styles");
664 assert_eq!($styles, &r[..])
665 }};
666 }
667
668 #[test]
669 fn two1() {
670 let s: &[Styles] = &[Bold, Underline];
671 test_combine!(s);
672 }
673
674 #[test]
675 fn two2() {
676 let s: &[Styles] = &[Underline, Italic];
677 test_combine!(s);
678 }
679
680 #[test]
681 fn two3() {
682 let s: &[Styles] = &[Bold, Italic];
683 test_combine!(s);
684 }
685
686 #[test]
687 fn three1() {
688 let s: &[Styles] = &[Bold, Underline, Italic];
689 test_combine!(s);
690 }
691
692 #[test]
693 fn three2() {
694 let s: &[Styles] = &[Dimmed, Underline, Italic];
695 test_combine!(s);
696 }
697
698 #[test]
699 fn four() {
700 let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden];
701 test_combine!(s);
702 }
703
704 #[test]
705 fn five() {
706 let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden];
707 test_combine!(s);
708 }
709
710 #[test]
711 fn six() {
712 let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden];
713 test_combine!(s);
714 }
715
716 #[test]
717 fn all() {
718 let s: &[Styles] = &[
719 Bold,
720 Dimmed,
721 Underline,
722 Reversed,
723 Italic,
724 Blink,
725 Hidden,
726 Strikethrough,
727 ];
728 test_combine!(s);
729 }
730 }
731
732 #[test]
733 fn test_style_contains() {
734 let mut style = Style(Styles::Bold.to_u8());
735 style.add(Styles::Italic);
736
737 assert!(style.contains(Styles::Bold));
738 assert!(style.contains(Styles::Italic));
739 assert!(!style.contains(Styles::Dimmed));
740 }
741
742 mod style_bitwise_logic {
743 use super::*;
744
745 macro_rules! check_impl {
746 ($lh:expr, $method:path, $rh:expr => $res:expr) => {
747 assert_eq!($method($lh, $rh), $res);
748 assert_eq!($method(&$lh, $rh), $res);
749 assert_eq!($method($lh, &$rh), $res);
750 assert_eq!($method(&$lh, &$rh), $res);
751 };
752 }
753
754 macro_rules! check_impl_reflexive {
755 ($lh:expr, $method:path, $rh:expr => $res:expr) => {
756 check_impl!($lh, $method, $rh => $res);
757 check_impl!($rh, $method, $lh => $res);
758 }
759 }
760
761 const TTABLE: (u8, u8) = (0b0101, 0b0011);
763
764 #[test]
765 fn binops() {
766 let tstyle_l = Style(TTABLE.0);
767 let tstyle_r = Style(TTABLE.1);
768 let and_res = Style(TTABLE.0 & TTABLE.1);
769 let or_res = Style(TTABLE.0 | TTABLE.1);
770 let xor_res = Style(TTABLE.0 ^ TTABLE.1);
771
772 check_impl!(tstyle_l, BitAnd::bitand, tstyle_r => and_res);
773 check_impl!(tstyle_l, BitOr::bitor, tstyle_r => or_res);
774 check_impl!(tstyle_l, BitXor::bitxor, tstyle_r => xor_res);
775 }
776
777 #[test]
778 fn binops_with_styles() {
779 let bold_underline = Style(0b0011);
780
781 check_impl_reflexive!(
782 bold_underline,
783 BitAnd::bitand,
784 Styles::Bold
785 => Style(0b0000_0001)
786 );
787 check_impl_reflexive!(
788 bold_underline,
789 BitOr::bitor,
790 Styles::Reversed
791 => Style(0b0000_0111)
792 );
793 check_impl_reflexive!(
794 bold_underline,
795 BitXor::bitxor,
796 Styles::Underline
797 => Style(0b0000_0001)
798 );
799 }
800
801 #[test]
802 fn not() {
803 let not_bold = !Style(BOLD);
804 assert!(!not_bold.contains(Styles::Bold));
805 assert!(not_bold.contains(Styles::Strikethrough));
806 assert_eq!(!Style(0b0011_0101), Style(0b1100_1010));
807 }
808
809 #[test]
810 fn assign_ops() {
811 let original_style = Style(0b0011);
812 let op_style = Style(0b0101);
813
814 let mut style = original_style;
815 style &= op_style;
816 assert_eq!(style, Style(0b0001));
817
818 style = original_style;
819 style |= op_style;
820 assert_eq!(style, Style(0b0111));
821
822 style = original_style;
823 style ^= op_style;
824 assert_eq!(style, Style(0b0110));
825 }
826
827 #[test]
828 fn assign_ops_with_styles() {
829 let original_style = Style(0b0011);
830
831 let mut style = original_style;
832 style &= Styles::Bold;
833 assert_eq!(style, Style(0b0001));
834
835 style = original_style;
836 style |= Styles::Reversed;
837 assert_eq!(style, Style(0b0111));
838
839 style = original_style;
840 style ^= Styles::Bold;
841 assert_eq!(style, Style(0b0010));
842 }
843
844 #[test]
845 fn styles_binops() {
846 check_impl!(
847 Styles::Bold,
848 BitAnd::bitand,
849 Styles::Bold
850 => Style(0b0000_0001)
851 );
852 assert_eq!(Styles::Bold & Styles::Underline, Style(0b0000_0000));
856
857 check_impl!(
858 Styles::Bold,
859 BitOr::bitor,
860 Styles::Underline
861 => Style(0b0000_0011)
862 );
863 assert_eq!(Styles::Bold | Styles::Bold, Style(0b0000_0001));
864
865 check_impl!(
866 Styles::Bold,
867 BitXor::bitxor,
868 Styles::Underline
869 => Style(0b0000_0011)
870 );
871 assert_eq!(Styles::Bold ^ Styles::Bold, Style(0b0000_0000));
872 }
873
874 #[test]
875 fn styles_not() {
876 let not_bold = !Styles::Bold;
877 assert_eq!(not_bold, Style(!BOLD));
878 }
879 }
880}