1use crate::{AnsiColors, Color, DynColor, DynColors};
2use core::fmt;
3
4#[cfg(doc)]
5use crate::OwoColorize;
6
7#[allow(missing_docs)]
9#[derive(Debug, Copy, Clone)]
10pub enum Effect {
11 Bold,
12 Dimmed,
13 Italic,
14 Underline,
15 Blink,
16 BlinkFast,
17 Reversed,
18 Hidden,
19 Strikethrough,
20}
21
22macro_rules! color_methods {
23 ($(
24 #[$fg_meta:meta] #[$bg_meta:meta] $color:ident $fg_method:ident $bg_method:ident
25 ),* $(,)?) => {
26 $(
27 #[$fg_meta]
28 #[must_use]
29 pub const fn $fg_method(mut self) -> Self {
30 self.fg = Some(DynColors::Ansi(AnsiColors::$color));
31 self
32 }
33
34 #[$fg_meta]
35 #[must_use]
36 pub const fn $bg_method(mut self) -> Self {
37 self.bg = Some(DynColors::Ansi(AnsiColors::$color));
38 self
39 }
40 )*
41 };
42}
43
44macro_rules! style_methods {
45 ($(#[$meta:meta] ($name:ident, $set_name:ident)),* $(,)?) => {
46 $(
47 #[$meta]
48 #[must_use]
49 pub const fn $name(mut self) -> Self {
50 self.style_flags = self.style_flags.$set_name(true);
51 self
52 }
53 )*
54 };
55}
56
57const _: () = (); pub struct Styled<T> {
61 pub(crate) target: T,
63 pub style: Style,
65}
66
67#[derive(Debug, Copy, Clone, PartialEq)]
83pub struct Style {
84 pub(crate) fg: Option<DynColors>,
85 pub(crate) bg: Option<DynColors>,
86 pub(crate) bold: bool,
87 pub(crate) style_flags: StyleFlags,
88}
89
90#[repr(transparent)]
91#[derive(Debug, Copy, Clone, PartialEq)]
92pub(crate) struct StyleFlags(pub(crate) u8);
93
94impl StyleFlags {
95 #[must_use]
96 #[inline]
97 const fn is_plain(&self) -> bool {
98 self.0 == 0
99 }
100}
101
102const DIMMED_SHIFT: u8 = 0;
103const ITALIC_SHIFT: u8 = 1;
104const UNDERLINE_SHIFT: u8 = 2;
105const BLINK_SHIFT: u8 = 3;
106const BLINK_FAST_SHIFT: u8 = 4;
107const REVERSED_SHIFT: u8 = 5;
108const HIDDEN_SHIFT: u8 = 6;
109const STRIKETHROUGH_SHIFT: u8 = 7;
110
111macro_rules! style_flags_methods {
112 ($(($shift:ident, $name:ident, $set_name:ident)),* $(,)?) => {
113 $(
114 #[must_use]
115 const fn $name(&self) -> bool {
116 ((self.0 >> $shift) & 1) != 0
117 }
118
119 #[must_use]
120 const fn $set_name(mut self, $name: bool) -> Self {
121 self.0 = (self.0 & !(1 << $shift)) | (($name as u8) << $shift);
122 self
123 }
124 )*
125 };
126}
127
128impl StyleFlags {
129 const fn new() -> Self {
130 Self(0)
131 }
132
133 style_flags_methods! {
134 (DIMMED_SHIFT, dimmed, set_dimmed),
135 (ITALIC_SHIFT, italic, set_italic),
136 (UNDERLINE_SHIFT, underline, set_underline),
137 (BLINK_SHIFT, blink, set_blink),
138 (BLINK_FAST_SHIFT, blink_fast, set_blink_fast),
139 (REVERSED_SHIFT, reversed, set_reversed),
140 (HIDDEN_SHIFT, hidden, set_hidden),
141 (STRIKETHROUGH_SHIFT, strikethrough, set_strikethrough),
142 }
143}
144
145impl Default for StyleFlags {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151impl Style {
152 #[must_use]
154 pub const fn new() -> Self {
155 Self {
156 fg: None,
157 bg: None,
158 bold: false,
159 style_flags: StyleFlags::new(),
160 }
161 }
162
163 pub const fn style<T>(&self, target: T) -> Styled<T> {
178 Styled {
179 target,
180 style: *self,
181 }
182 }
183
184 #[must_use]
192 pub const fn fg<C: Color>(mut self) -> Self {
193 self.fg = Some(C::DYN_COLORS_EQUIVALENT);
194 self
195 }
196
197 #[must_use]
205 pub const fn bg<C: Color>(mut self) -> Self {
206 self.bg = Some(C::DYN_COLORS_EQUIVALENT);
207 self
208 }
209
210 #[must_use]
216 pub const fn remove_fg(mut self) -> Self {
217 self.fg = None;
218 self
219 }
220
221 #[must_use]
227 pub const fn remove_bg(mut self) -> Self {
228 self.bg = None;
229 self
230 }
231
232 color_methods! {
233 Black black on_black,
236 Red red on_red,
239 Green green on_green,
242 Yellow yellow on_yellow,
245 Blue blue on_blue,
248 Magenta magenta on_magenta,
251 Magenta purple on_purple,
254 Cyan cyan on_cyan,
257 White white on_white,
260
261 Default default_color on_default_color,
264
265 BrightBlack bright_black on_bright_black,
268 BrightRed bright_red on_bright_red,
271 BrightGreen bright_green on_bright_green,
274 BrightYellow bright_yellow on_bright_yellow,
277 BrightBlue bright_blue on_bright_blue,
280 BrightMagenta bright_magenta on_bright_magenta,
283 BrightMagenta bright_purple on_bright_purple,
286 BrightCyan bright_cyan on_bright_cyan,
289 BrightWhite bright_white on_bright_white,
292 }
293
294 #[must_use]
296 pub const fn bold(mut self) -> Self {
297 self.bold = true;
298 self
299 }
300
301 style_methods! {
302 (dimmed, set_dimmed),
304 (italic, set_italic),
306 (underline, set_underline),
308 (blink, set_blink),
310 (blink_fast, set_blink_fast),
312 (reversed, set_reversed),
314 (hidden, set_hidden),
316 (strikethrough, set_strikethrough),
318 }
319
320 #[must_use]
321 const fn set_effect(mut self, effect: Effect, to: bool) -> Self {
322 use Effect::*;
323 match effect {
324 Bold => {
325 self.bold = to;
326 }
327 Dimmed => {
328 self.style_flags = self.style_flags.set_dimmed(to);
331 }
332 Italic => {
333 self.style_flags = self.style_flags.set_italic(to);
334 }
335 Underline => {
336 self.style_flags = self.style_flags.set_underline(to);
337 }
338 Blink => {
339 self.style_flags = self.style_flags.set_blink(to);
340 }
341 BlinkFast => {
342 self.style_flags = self.style_flags.set_blink_fast(to);
343 }
344 Reversed => {
345 self.style_flags = self.style_flags.set_reversed(to);
346 }
347 Hidden => {
348 self.style_flags = self.style_flags.set_hidden(to);
349 }
350 Strikethrough => {
351 self.style_flags = self.style_flags.set_strikethrough(to);
352 }
353 }
354 self
355 }
356
357 #[must_use]
358 const fn set_effects(mut self, mut effects: &[Effect], to: bool) -> Self {
359 while let [first, rest @ ..] = effects {
361 self = self.set_effect(*first, to);
362 effects = rest;
363 }
364 self
365 }
366
367 #[must_use]
369 pub const fn effect(self, effect: Effect) -> Self {
370 self.set_effect(effect, true)
371 }
372
373 #[must_use]
375 pub const fn remove_effect(self, effect: Effect) -> Self {
376 self.set_effect(effect, false)
377 }
378
379 #[must_use]
381 pub const fn effects(self, effects: &[Effect]) -> Self {
382 self.set_effects(effects, true)
383 }
384
385 #[must_use]
387 pub const fn remove_effects(self, effects: &[Effect]) -> Self {
388 self.set_effects(effects, false)
389 }
390
391 #[must_use]
393 pub const fn remove_all_effects(mut self) -> Self {
394 self.bold = false;
395 self.style_flags = StyleFlags::new();
396 self
397 }
398
399 #[must_use]
409 pub fn color<Color: DynColor>(mut self, color: Color) -> Self {
410 self.fg = Some(color.get_dyncolors_fg());
412 self
413 }
414
415 #[must_use]
425 pub fn on_color<Color: DynColor>(mut self, color: Color) -> Self {
426 self.bg = Some(color.get_dyncolors_bg());
428 self
429 }
430
431 #[must_use]
433 pub const fn fg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
434 self.fg = Some(DynColors::Rgb(R, G, B));
435
436 self
437 }
438
439 #[must_use]
441 pub const fn bg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
442 self.bg = Some(DynColors::Rgb(R, G, B));
443
444 self
445 }
446
447 #[must_use]
449 pub const fn truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
450 self.fg = Some(DynColors::Rgb(r, g, b));
451 self
452 }
453
454 #[must_use]
456 pub const fn on_truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
457 self.bg = Some(DynColors::Rgb(r, g, b));
458 self
459 }
460
461 #[must_use]
463 #[inline]
464 pub const fn is_plain(&self) -> bool {
465 let s = &self;
466 !(s.fg.is_some() || s.bg.is_some() || s.bold) && s.style_flags.is_plain()
467 }
468
469 pub const fn prefix_formatter(&self) -> StylePrefixFormatter {
492 StylePrefixFormatter(*self)
493 }
494
495 pub const fn suffix_formatter(&self) -> StyleSuffixFormatter {
503 StyleSuffixFormatter(*self)
504 }
505
506 #[inline]
508 #[allow(unused_assignments)]
509 pub fn fmt_prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 let s = self;
511 let format_less_important_effects = s.style_flags != StyleFlags::default();
512 let format_effect = s.bold || format_less_important_effects;
513 let format_any = !self.is_plain();
514
515 let mut semicolon = false;
516
517 if format_any {
518 f.write_str("\x1b[")?;
519 }
520
521 if let Some(fg) = s.fg {
522 <DynColors as DynColor>::fmt_raw_ansi_fg(&fg, f)?;
523 semicolon = true;
524 }
525
526 if let Some(bg) = s.bg {
527 if s.fg.is_some() {
528 f.write_str(";")?;
529 }
530 <DynColors as DynColor>::fmt_raw_ansi_bg(&bg, f)?;
531 semicolon = true;
532 }
533
534 if format_effect {
535 if s.bold {
536 if semicolon {
537 f.write_str(";")?;
538 }
539
540 f.write_str("1")?;
541
542 semicolon = true;
543 }
544
545 macro_rules! text_effect_fmt {
546 ($style:ident, $formatter:ident, $semicolon:ident, $(($attr:ident, $value:literal)),* $(,)?) => {
547 $(
548 if $style.style_flags.$attr() {
549 if $semicolon {
550 $formatter.write_str(";")?;
551 }
552 $formatter.write_str($value)?;
553
554 $semicolon = true;
555 }
556 )+
557 }
558 }
559
560 if format_less_important_effects {
561 text_effect_fmt! {
562 s, f, semicolon,
563 (dimmed, "2"),
564 (italic, "3"),
565 (underline, "4"),
566 (blink, "5"),
567 (blink_fast, "6"),
568 (reversed, "7"),
569 (hidden, "8"),
570 (strikethrough, "9"),
571 }
572 }
573 }
574
575 if format_any {
576 f.write_str("m")?;
577 }
578 Ok(())
579 }
580
581 #[inline]
583 pub fn fmt_suffix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584 if !self.is_plain() {
585 f.write_str("\x1b[0m")?;
586 }
587 Ok(())
588 }
589}
590
591#[derive(Debug, Clone, Copy, PartialEq)]
596#[must_use = "this formatter does nothing unless displayed"]
597pub struct StylePrefixFormatter(Style);
598
599impl fmt::Display for StylePrefixFormatter {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 self.0.fmt_prefix(f)
602 }
603}
604
605#[derive(Debug, Clone, Copy, PartialEq)]
610#[must_use = "this formatter does nothing unless displayed"]
611pub struct StyleSuffixFormatter(Style);
612
613impl fmt::Display for StyleSuffixFormatter {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 self.0.fmt_suffix(f)
616 }
617}
618
619impl Default for Style {
620 fn default() -> Self {
621 Self::new()
622 }
623}
624
625pub const fn style() -> Style {
627 Style::new()
628}
629
630impl<T> Styled<T> {
631 pub const fn inner(&self) -> &T {
633 &self.target
634 }
635
636 #[cfg(const_mut_refs)]
640 pub const fn inner_mut(&mut self) -> &mut T {
641 &mut self.target
642 }
643
644 #[cfg(not(const_mut_refs))]
648 pub fn inner_mut(&mut self) -> &mut T {
649 &mut self.target
650 }
651}
652
653macro_rules! impl_fmt {
654 ($($trait:path),* $(,)?) => {
655 $(
656 impl<T: $trait> $trait for Styled<T> {
657 #[allow(unused_assignments)]
658 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659 self.style.fmt_prefix(f)?;
660 <T as $trait>::fmt(&self.target, f)?;
661 self.style.fmt_suffix(f)
662 }
663 }
664 )*
665 };
666}
667
668impl_fmt! {
669 fmt::Display,
670 fmt::Debug,
671 fmt::UpperHex,
672 fmt::LowerHex,
673 fmt::Binary,
674 fmt::UpperExp,
675 fmt::LowerExp,
676 fmt::Octal,
677 fmt::Pointer,
678}
679
680#[cfg(test)]
681mod tests {
682 use super::*;
683 use crate::{AnsiColors, OwoColorize};
684
685 #[test]
686 fn size_of() {
687 let size = std::mem::size_of::<Style>();
688 assert_eq!(size, 10, "size of Style should be 10 bytes");
689 }
690
691 #[test]
692 fn test_it() {
693 let style = Style::new()
694 .bright_white()
695 .on_blue()
696 .bold()
697 .dimmed()
698 .italic()
699 .underline()
700 .blink()
701 .strikethrough();
705 let s = style.style("TEST");
706 let s2 = format!("{}", &s);
707 println!("{}", &s2);
708 assert_eq!(&s2, "\u{1b}[97;44;1;2;3;4;5;9mTEST\u{1b}[0m");
709
710 let prefix = format!("{}", style.prefix_formatter());
711 assert_eq!(&prefix, "\u{1b}[97;44;1;2;3;4;5;9m");
712
713 let suffix = format!("{}", style.suffix_formatter());
714 assert_eq!(&suffix, "\u{1b}[0m");
715 }
716
717 #[test]
718 fn test_effects() {
719 use Effect::*;
720 let style = Style::new().effects(&[Strikethrough, Underline]);
721
722 let s = style.style("TEST");
723 let s2 = format!("{}", &s);
724 println!("{}", &s2);
725 assert_eq!(&s2, "\u{1b}[4;9mTEST\u{1b}[0m");
726 }
727
728 #[test]
729 fn test_color() {
730 let style = Style::new()
731 .color(AnsiColors::White)
732 .on_color(AnsiColors::Black);
733
734 let s = style.style("TEST");
735 let s2 = format!("{}", &s);
736 println!("{}", &s2);
737 assert_eq!(&s2, "\u{1b}[37;40mTEST\u{1b}[0m");
738 }
739
740 #[test]
741 fn test_truecolor() {
742 let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
743
744 let s = style.style("TEST");
745 let s2 = format!("{}", &s);
746 println!("{}", &s2);
747 assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
748 }
749
750 #[test]
751 fn test_string_reference() {
752 let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
753
754 let string = String::from("TEST");
755 let s = style.style(&string);
756 let s2 = format!("{}", &s);
757 println!("{}", &s2);
758 assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
759 }
760
761 #[test]
762 fn test_owocolorize() {
763 let style = Style::new().bright_white().on_blue();
764
765 let s = "TEST".style(style);
766 let s2 = format!("{}", &s);
767 println!("{}", &s2);
768 assert_eq!(&s2, "\u{1b}[97;44mTEST\u{1b}[0m");
769 }
770
771 #[test]
772 fn test_is_plain() {
773 let style = Style::new().bright_white().on_blue();
774
775 assert!(!style.is_plain());
776 assert!(Style::default().is_plain());
777
778 let string = String::from("TEST");
779 let s = Style::default().style(&string);
780 let s2 = format!("{}", &s);
781
782 assert_eq!(string, s2)
783 }
784
785 #[test]
786 fn test_inner() {
787 let style = Style::default();
788
789 let mut s = "TEST".style(style);
790
791 assert_eq!(&&"TEST", s.inner());
792
793 *s.inner_mut() = &"changed";
794 assert_eq!(&&"changed", s.inner());
795 assert_eq!("changed", format!("{}", s));
796 }
797}