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>,
18 pub bg: Option<Color>,
20 pub bold: bool,
22 pub italic: bool,
24 pub underline: bool,
26 pub dim: bool,
28}
29
30impl Style {
31 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn fg(mut self, color: Color) -> Self {
38 self.fg = Some(color);
39 self
40 }
41
42 pub fn bg(mut self, color: Color) -> Self {
44 self.bg = Some(color);
45 self
46 }
47
48 pub fn bold(mut self) -> Self {
50 self.bold = true;
51 self
52 }
53
54 pub fn italic(mut self) -> Self {
56 self.italic = true;
57 self
58 }
59
60 pub fn underline(mut self) -> Self {
62 self.underline = true;
63 self
64 }
65
66 pub fn dim(mut self) -> Self {
68 self.dim = true;
69 self
70 }
71
72 pub fn prefix(&self, mode: ColorMode) -> String {
74 let mut parts = Vec::new();
75 if let Some(c) = self.fg {
76 parts.push(ansi::fg(c, mode));
77 }
78 if let Some(c) = self.bg {
79 parts.push(ansi::bg(c, mode));
80 }
81 if self.bold {
82 parts.push("\x1b[1m".to_string());
83 }
84 if self.dim {
85 parts.push("\x1b[2m".to_string());
86 }
87 if self.italic {
88 parts.push("\x1b[3m".to_string());
89 }
90 if self.underline {
91 parts.push("\x1b[4m".to_string());
92 }
93 parts.concat()
94 }
95
96 pub const fn suffix() -> &'static str {
98 ansi::RESET
99 }
100}
101
102pub fn stylize(text: &str, style: &Style) -> String {
105 let mode = ColorMode::detect();
106 format!("{}{}{}", style.prefix(mode), text, Style::suffix())
107}
108
109pub fn stylize_padded(text: &str, style: &Style, pad: usize) -> String {
111 let padding = " ".repeat(pad);
112 stylize(&format!("{}{}{}", padding, text, padding), style)
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn style_builder() {
121 let s = Style::new().fg(Color::SUNBEAM_ORANGE).bold();
122 assert_eq!(s.fg, Some(Color::SUNBEAM_ORANGE));
123 assert!(s.bold);
124 assert!(!s.italic);
125 }
126
127 #[test]
128 fn stylize_produces_codes() {
129 let s = Style::new().fg(Color::SUNBEAM_ORANGE);
130 let out = stylize("hi", &s);
131 assert!(out.contains("hi"));
132 assert!(out.starts_with('\x1b'));
133 assert!(out.ends_with("\x1b[0m"));
134 }
135
136 #[test]
137 fn stylize_padded_applies_padding() {
138 let s = Style::new().fg(Color::WHITE);
139 let out = stylize_padded("ok", &s, 2);
140 assert!(out.contains(" ok "));
141 }
142}