1use crate::{AnsiColors, Color, DynColor, DynColors};
2use core::fmt;
3
4#[cfg(doc)]
5use crate::OwoColorize;
6
7/// A runtime-configurable text effect for use with [`Style`]
8#[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 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 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 fn $name(mut self) -> Self {
50 self.style_flags.$set_name(true);
51 self
52 }
53 )*
54 };
55}
56
57const _: () = (); // workaround for syntax highlighting bug
58
59/// A wrapper type which applies a [`Style`] when displaying the inner type
60pub struct Styled<T> {
61 /// The target value to be styled
62 pub(crate) target: T,
63 /// The style to apply to target
64 pub style: Style,
65}
66
67/// A pre-computed style that can be applied to a struct using [`OwoColorize::style`]. Its
68/// interface mimicks that of [`OwoColorize`], but instead of chaining methods on your
69/// object, you instead chain them on the `Style` object before applying it.
70///
71/// ```rust
72/// use owo_colors::{OwoColorize, Style};
73///
74/// let my_style = Style::new()
75/// .red()
76/// .on_white()
77/// .strikethrough();
78///
79/// println!("{}", "red text, white background, struck through".style(my_style));
80/// ```
81#[derive(Debug, Default, Copy, Clone, PartialEq)]
82pub struct Style {
83 pub(crate) fg: Option<DynColors>,
84 pub(crate) bg: Option<DynColors>,
85 pub(crate) bold: bool,
86 pub(crate) style_flags: StyleFlags,
87}
88
89#[repr(transparent)]
90#[derive(Debug, Default, Copy, Clone, PartialEq)]
91pub(crate) struct StyleFlags(pub(crate) u8);
92
93const DIMMED_SHIFT: u8 = 0;
94const ITALIC_SHIFT: u8 = 1;
95const UNDERLINE_SHIFT: u8 = 2;
96const BLINK_SHIFT: u8 = 3;
97const BLINK_FAST_SHIFT: u8 = 4;
98const REVERSED_SHIFT: u8 = 5;
99const HIDDEN_SHIFT: u8 = 6;
100const STRIKETHROUGH_SHIFT: u8 = 7;
101
102macro_rules! style_flags_methods {
103 ($(($shift:ident, $name:ident, $set_name:ident)),* $(,)?) => {
104 $(
105 fn $name(&self) -> bool {
106 ((self.0 >> $shift) & 1) != 0
107 }
108
109 fn $set_name(&mut self, $name: bool) {
110 self.0 = (self.0 & !(1 << $shift)) | (($name as u8) << $shift);
111 }
112 )*
113 };
114}
115
116impl StyleFlags {
117 style_flags_methods! {
118 (DIMMED_SHIFT, dimmed, set_dimmed),
119 (ITALIC_SHIFT, italic, set_italic),
120 (UNDERLINE_SHIFT, underline, set_underline),
121 (BLINK_SHIFT, blink, set_blink),
122 (BLINK_FAST_SHIFT, blink_fast, set_blink_fast),
123 (REVERSED_SHIFT, reversed, set_reversed),
124 (HIDDEN_SHIFT, hidden, set_hidden),
125 (STRIKETHROUGH_SHIFT, strikethrough, set_strikethrough),
126 }
127}
128
129impl Style {
130 /// Create a new style to be applied later
131 #[must_use]
132 pub fn new() -> Self {
133 Self::default()
134 }
135
136 /// Apply the style to a given struct to output
137 pub fn style<T>(&self, target: T) -> Styled<T> {
138 Styled {
139 target,
140 style: *self,
141 }
142 }
143
144 /// Set the foreground color generically
145 ///
146 /// ```rust
147 /// use owo_colors::{OwoColorize, colors::*};
148 ///
149 /// println!("{}", "red foreground".fg::<Red>());
150 /// ```
151 #[must_use]
152 pub fn fg<C: Color>(mut self) -> Self {
153 self.fg = Some(C::into_dyncolors());
154 self
155 }
156
157 /// Set the background color generically.
158 ///
159 /// ```rust
160 /// use owo_colors::{OwoColorize, colors::*};
161 ///
162 /// println!("{}", "black background".bg::<Black>());
163 /// ```
164 #[must_use]
165 pub fn bg<C: Color>(mut self) -> Self {
166 self.bg = Some(C::into_dyncolors());
167 self
168 }
169
170 /// Removes the foreground color from the style. Note that this does not apply
171 /// the default color, but rather represents not changing the current terminal color.
172 ///
173 /// If you wish to actively change the terminal color back to the default, see
174 /// [`Style::default_color`].
175 #[must_use]
176 pub fn remove_fg(mut self) -> Self {
177 self.fg = None;
178 self
179 }
180
181 /// Removes the background color from the style. Note that this does not apply
182 /// the default color, but rather represents not changing the current terminal color.
183 ///
184 /// If you wish to actively change the terminal color back to the default, see
185 /// [`Style::on_default_color`].
186 #[must_use]
187 pub fn remove_bg(mut self) -> Self {
188 self.bg = None;
189 self
190 }
191
192 color_methods! {
193 /// Change the foreground color to black
194 /// Change the background color to black
195 Black black on_black,
196 /// Change the foreground color to red
197 /// Change the background color to red
198 Red red on_red,
199 /// Change the foreground color to green
200 /// Change the background color to green
201 Green green on_green,
202 /// Change the foreground color to yellow
203 /// Change the background color to yellow
204 Yellow yellow on_yellow,
205 /// Change the foreground color to blue
206 /// Change the background color to blue
207 Blue blue on_blue,
208 /// Change the foreground color to magenta
209 /// Change the background color to magenta
210 Magenta magenta on_magenta,
211 /// Change the foreground color to purple
212 /// Change the background color to purple
213 Magenta purple on_purple,
214 /// Change the foreground color to cyan
215 /// Change the background color to cyan
216 Cyan cyan on_cyan,
217 /// Change the foreground color to white
218 /// Change the background color to white
219 White white on_white,
220
221 /// Change the foreground color to the terminal default
222 /// Change the background color to the terminal default
223 Default default_color on_default_color,
224
225 /// Change the foreground color to bright black
226 /// Change the background color to bright black
227 BrightBlack bright_black on_bright_black,
228 /// Change the foreground color to bright red
229 /// Change the background color to bright red
230 BrightRed bright_red on_bright_red,
231 /// Change the foreground color to bright green
232 /// Change the background color to bright green
233 BrightGreen bright_green on_bright_green,
234 /// Change the foreground color to bright yellow
235 /// Change the background color to bright yellow
236 BrightYellow bright_yellow on_bright_yellow,
237 /// Change the foreground color to bright blue
238 /// Change the background color to bright blue
239 BrightBlue bright_blue on_bright_blue,
240 /// Change the foreground color to bright magenta
241 /// Change the background color to bright magenta
242 BrightMagenta bright_magenta on_bright_magenta,
243 /// Change the foreground color to bright purple
244 /// Change the background color to bright purple
245 BrightMagenta bright_purple on_bright_purple,
246 /// Change the foreground color to bright cyan
247 /// Change the background color to bright cyan
248 BrightCyan bright_cyan on_bright_cyan,
249 /// Change the foreground color to bright white
250 /// Change the background color to bright white
251 BrightWhite bright_white on_bright_white,
252 }
253
254 /// Make the text bold
255 #[must_use]
256 pub fn bold(mut self) -> Self {
257 self.bold = true;
258 self
259 }
260
261 style_methods! {
262 /// Make the text dim
263 (dimmed, set_dimmed),
264 /// Make the text italicized
265 (italic, set_italic),
266 /// Make the text italicized
267 (underline, set_underline),
268 /// Make the text blink
269 (blink, set_blink),
270 /// Make the text blink (but fast!)
271 (blink_fast, set_blink_fast),
272 /// Swap the foreground and background colors
273 (reversed, set_reversed),
274 /// Hide the text
275 (hidden, set_hidden),
276 /// Cross out the text
277 (strikethrough, set_strikethrough),
278 }
279
280 fn set_effect(&mut self, effect: Effect, to: bool) {
281 use Effect::*;
282 match effect {
283 Bold => self.bold = to,
284 Dimmed => self.style_flags.set_dimmed(to),
285 Italic => self.style_flags.set_italic(to),
286 Underline => self.style_flags.set_underline(to),
287 Blink => self.style_flags.set_blink(to),
288 BlinkFast => self.style_flags.set_blink_fast(to),
289 Reversed => self.style_flags.set_reversed(to),
290 Hidden => self.style_flags.set_hidden(to),
291 Strikethrough => self.style_flags.set_strikethrough(to),
292 }
293 }
294
295 fn set_effects(&mut self, effects: &[Effect], to: bool) {
296 for e in effects {
297 self.set_effect(*e, to)
298 }
299 }
300
301 /// Apply a given effect from the style
302 #[must_use]
303 pub fn effect(mut self, effect: Effect) -> Self {
304 self.set_effect(effect, true);
305 self
306 }
307
308 /// Remove a given effect from the style
309 #[must_use]
310 pub fn remove_effect(mut self, effect: Effect) -> Self {
311 self.set_effect(effect, false);
312 self
313 }
314
315 /// Apply a given set of effects to the style
316 #[must_use]
317 pub fn effects(mut self, effects: &[Effect]) -> Self {
318 self.set_effects(effects, true);
319 self
320 }
321
322 /// Remove a given set of effects from the style
323 #[must_use]
324 pub fn remove_effects(mut self, effects: &[Effect]) -> Self {
325 self.set_effects(effects, false);
326 self
327 }
328
329 /// Disables all the given effects from the style
330 #[must_use]
331 pub fn remove_all_effects(mut self) -> Self {
332 self.bold = false;
333 self.style_flags = StyleFlags::default();
334 self
335 }
336
337 /// Set the foreground color at runtime. Only use if you do not know which color will be used at
338 /// compile-time. If the color is constant, use either [`OwoColorize::fg`](crate::OwoColorize::fg) or
339 /// a color-specific method, such as [`OwoColorize::green`](crate::OwoColorize::green),
340 ///
341 /// ```rust
342 /// use owo_colors::{OwoColorize, AnsiColors};
343 ///
344 /// println!("{}", "green".color(AnsiColors::Green));
345 /// ```
346 #[must_use]
347 pub fn color<Color: DynColor>(mut self, color: Color) -> Self {
348 self.fg = Some(color.get_dyncolors_fg());
349 self
350 }
351
352 /// Set the background color at runtime. Only use if you do not know what color to use at
353 /// compile-time. If the color is constant, use either [`OwoColorize::bg`](crate::OwoColorize::bg) or
354 /// a color-specific method, such as [`OwoColorize::on_yellow`](crate::OwoColorize::on_yellow),
355 ///
356 /// ```rust
357 /// use owo_colors::{OwoColorize, AnsiColors};
358 ///
359 /// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
360 /// ```
361 #[must_use]
362 pub fn on_color<Color: DynColor>(mut self, color: Color) -> Self {
363 self.bg = Some(color.get_dyncolors_bg());
364 self
365 }
366
367 /// Set the foreground color to a specific RGB value.
368 #[must_use]
369 pub fn fg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
370 self.fg = Some(DynColors::Rgb(R, G, B));
371
372 self
373 }
374
375 /// Set the background color to a specific RGB value.
376 #[must_use]
377 pub fn bg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
378 self.bg = Some(DynColors::Rgb(R, G, B));
379
380 self
381 }
382
383 /// Sets the foreground color to an RGB value.
384 #[must_use]
385 pub fn truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
386 self.fg = Some(DynColors::Rgb(r, g, b));
387 self
388 }
389
390 /// Sets the background color to an RGB value.
391 #[must_use]
392 pub fn on_truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
393 self.bg = Some(DynColors::Rgb(r, g, b));
394 self
395 }
396
397 /// Returns if the style does not apply any formatting
398 #[must_use]
399 #[inline]
400 pub fn is_plain(&self) -> bool {
401 let s = &self;
402 !(s.fg.is_some() || s.bg.is_some() || s.bold || s.style_flags != StyleFlags::default())
403 }
404
405 /// Applies the ANSI-prefix for this style to the given formatter
406 #[inline]
407 #[allow(unused_assignments)]
408 pub fn fmt_prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409 let s = self;
410 let format_less_important_effects = s.style_flags != StyleFlags::default();
411 let format_effect = s.bold || format_less_important_effects;
412 let format_any = !self.is_plain();
413
414 let mut semicolon = false;
415
416 if format_any {
417 f.write_str("\x1b[")?;
418 }
419
420 if let Some(fg) = s.fg {
421 <DynColors as DynColor>::fmt_raw_ansi_fg(&fg, f)?;
422 semicolon = true;
423 }
424
425 if let Some(bg) = s.bg {
426 if s.fg.is_some() {
427 f.write_str(";")?;
428 }
429 <DynColors as DynColor>::fmt_raw_ansi_bg(&bg, f)?;
430 }
431
432 if format_effect {
433 if s.bold {
434 if semicolon {
435 f.write_str(";")?;
436 }
437
438 f.write_str("1")?;
439
440 semicolon = true;
441 }
442
443 macro_rules! text_effect_fmt {
444 ($style:ident, $formatter:ident, $semicolon:ident, $(($attr:ident, $value:literal)),* $(,)?) => {
445 $(
446 if $style.style_flags.$attr() {
447 if $semicolon {
448 $formatter.write_str(";")?;
449 }
450 $formatter.write_str($value)?;
451
452 $semicolon = true;
453 }
454 )+
455 }
456 }
457
458 if format_less_important_effects {
459 text_effect_fmt! {
460 s, f, semicolon,
461 (dimmed, "2"),
462 (italic, "3"),
463 (underline, "4"),
464 (blink, "5"),
465 (blink_fast, "6"),
466 (reversed, "7"),
467 (hidden, "8"),
468 (strikethrough, "9"),
469 }
470 }
471 }
472
473 if format_any {
474 f.write_str("m")?;
475 }
476 Ok(())
477 }
478
479 /// Applies the ANSI-suffix for this style to the given formatter
480 #[inline]
481 pub fn fmt_suffix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 if !self.is_plain() {
483 f.write_str("\x1b[0m")?;
484 }
485 Ok(())
486 }
487}
488
489/// Helper to create [`Style`]s more ergonomically
490pub fn style() -> Style {
491 Style::new()
492}
493
494impl<T> Styled<T> {
495 /// Returns a reference to the inner value to be styled
496 pub fn inner(&self) -> &T {
497 &self.target
498 }
499
500 /// Returns a mutable reference to the inner value to be styled
501 pub fn inner_mut(&mut self) -> &mut T {
502 &mut self.target
503 }
504}
505
506macro_rules! impl_fmt {
507 ($($trait:path),* $(,)?) => {
508 $(
509 impl<T: $trait> $trait for Styled<T> {
510 #[allow(unused_assignments)]
511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512 self.style.fmt_prefix(f)?;
513 <T as $trait>::fmt(&self.target, f)?;
514 self.style.fmt_suffix(f)
515 }
516 }
517 )*
518 };
519}
520
521impl_fmt! {
522 fmt::Display,
523 fmt::Debug,
524 fmt::UpperHex,
525 fmt::LowerHex,
526 fmt::Binary,
527 fmt::UpperExp,
528 fmt::LowerExp,
529 fmt::Octal,
530 fmt::Pointer,
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536 use crate::{AnsiColors, OwoColorize};
537
538 struct StylePrefixOnly(Style);
539 impl fmt::Display for StylePrefixOnly {
540 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
541 self.0.fmt_prefix(f)
542 }
543 }
544
545 struct StyleSuffixOnly(Style);
546 impl fmt::Display for StyleSuffixOnly {
547 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548 self.0.fmt_suffix(f)
549 }
550 }
551
552 #[test]
553 fn test_it() {
554 let style = Style::new()
555 .bright_white()
556 .on_blue()
557 .bold()
558 .dimmed()
559 .italic()
560 .underline()
561 .blink()
562 //.blink_fast()
563 //.reversed()
564 //.hidden()
565 .strikethrough();
566 let s = style.style("TEST");
567 let s2 = format!("{}", &s);
568 println!("{}", &s2);
569 assert_eq!(&s2, "\u{1b}[97;44;1;2;3;4;5;9mTEST\u{1b}[0m");
570
571 let prefix = format!("{}", StylePrefixOnly(style));
572 assert_eq!(&prefix, "\u{1b}[97;44;1;2;3;4;5;9m");
573
574 let suffix = format!("{}", StyleSuffixOnly(style));
575 assert_eq!(&suffix, "\u{1b}[0m");
576 }
577
578 #[test]
579 fn test_effects() {
580 use Effect::*;
581 let style = Style::new().effects(&[Strikethrough, Underline]);
582
583 let s = style.style("TEST");
584 let s2 = format!("{}", &s);
585 println!("{}", &s2);
586 assert_eq!(&s2, "\u{1b}[4;9mTEST\u{1b}[0m");
587 }
588
589 #[test]
590 fn test_color() {
591 let style = Style::new()
592 .color(AnsiColors::White)
593 .on_color(AnsiColors::Black);
594
595 let s = style.style("TEST");
596 let s2 = format!("{}", &s);
597 println!("{}", &s2);
598 assert_eq!(&s2, "\u{1b}[37;40mTEST\u{1b}[0m");
599 }
600
601 #[test]
602 fn test_truecolor() {
603 let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
604
605 let s = style.style("TEST");
606 let s2 = format!("{}", &s);
607 println!("{}", &s2);
608 assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
609 }
610
611 #[test]
612 fn test_string_reference() {
613 let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
614
615 let string = String::from("TEST");
616 let s = style.style(&string);
617 let s2 = format!("{}", &s);
618 println!("{}", &s2);
619 assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
620 }
621
622 #[test]
623 fn test_owocolorize() {
624 let style = Style::new().bright_white().on_blue();
625
626 let s = "TEST".style(style);
627 let s2 = format!("{}", &s);
628 println!("{}", &s2);
629 assert_eq!(&s2, "\u{1b}[97;44mTEST\u{1b}[0m");
630 }
631
632 #[test]
633 fn test_is_plain() {
634 let style = Style::new().bright_white().on_blue();
635
636 assert!(!style.is_plain());
637 assert!(Style::default().is_plain());
638
639 let string = String::from("TEST");
640 let s = Style::default().style(&string);
641 let s2 = format!("{}", &s);
642
643 assert_eq!(string, s2)
644 }
645
646 #[test]
647 fn test_inner() {
648 let style = Style::default();
649
650 let mut s = "TEST".style(style);
651
652 assert_eq!(&&"TEST", s.inner());
653
654 *s.inner_mut() = &"changed";
655 assert_eq!(&&"changed", s.inner());
656 assert_eq!("changed", format!("{}", s));
657 }
658}
659