1use super::{
8 Color,
9 ColorMode,
10 ansi,
11};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
15pub struct Style {
16 pub fg: Option<Color>,
17 pub bg: Option<Color>,
18 pub bold: bool,
19 pub italic: bool,
20 pub underline: bool,
21 pub dim: bool,
22}
23
24impl Style {
25 pub fn new() -> Self {
26 Self::default()
27 }
28
29 pub fn fg(mut self, color: Color) -> Self {
30 self.fg = Some(color);
31 self
32 }
33
34 pub fn bg(mut self, color: Color) -> Self {
35 self.bg = Some(color);
36 self
37 }
38
39 pub fn bold(mut self) -> Self {
40 self.bold = true;
41 self
42 }
43
44 pub fn italic(mut self) -> Self {
45 self.italic = true;
46 self
47 }
48
49 pub fn underline(mut self) -> Self {
50 self.underline = true;
51 self
52 }
53
54 pub fn dim(mut self) -> Self {
55 self.dim = true;
56 self
57 }
58
59 pub fn prefix(&self, mode: ColorMode) -> String {
61 let mut parts = Vec::new();
62 if let Some(c) = self.fg {
63 parts.push(ansi::fg(c, mode));
64 }
65 if let Some(c) = self.bg {
66 parts.push(ansi::bg(c, mode));
67 }
68 if self.bold {
69 parts.push("\x1b[1m".to_string());
70 }
71 if self.dim {
72 parts.push("\x1b[2m".to_string());
73 }
74 if self.italic {
75 parts.push("\x1b[3m".to_string());
76 }
77 if self.underline {
78 parts.push("\x1b[4m".to_string());
79 }
80 parts.concat()
81 }
82
83 pub const fn suffix() -> &'static str {
85 ansi::RESET
86 }
87}
88
89pub fn stylize(text: &str, style: &Style) -> String {
92 let mode = ColorMode::detect();
93 format!("{}{}{}", style.prefix(mode), text, Style::suffix())
94}
95
96pub fn stylize_padded(text: &str, style: &Style, pad: usize) -> String {
98 let padding = " ".repeat(pad);
99 stylize(&format!("{}{}{}", padding, text, padding), style)
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn style_builder() {
108 let s = Style::new().fg(Color::SUNBEAM_ORANGE).bold();
109 assert_eq!(s.fg, Some(Color::SUNBEAM_ORANGE));
110 assert!(s.bold);
111 assert!(!s.italic);
112 }
113
114 #[test]
115 fn stylize_produces_codes() {
116 let s = Style::new().fg(Color::SUNBEAM_ORANGE);
117 let out = stylize("hi", &s);
118 assert!(out.contains("hi"));
119 assert!(out.starts_with('\x1b'));
120 assert!(out.ends_with("\x1b[0m"));
121 }
122
123 #[test]
124 fn stylize_padded_applies_padding() {
125 let s = Style::new().fg(Color::WHITE);
126 let out = stylize_padded("ok", &s, 2);
127 assert!(out.contains(" ok "));
128 }
129}