1use crossterm::style as crossterm_style;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Color {
10 Black,
12 Red,
14 Green,
16 Yellow,
18 Blue,
20 Magenta,
22 Cyan,
24 White,
26 BrightBlack,
28 BrightRed,
30 BrightGreen,
32 BrightYellow,
34 BrightBlue,
36 BrightMagenta,
38 BrightCyan,
40 BrightWhite,
42 Rgb(u8, u8, u8),
44}
45
46impl From<Color> for crossterm_style::Color {
47 fn from(color: Color) -> Self {
48 match color {
49 Color::Black => crossterm_style::Color::Black,
50 Color::Red => crossterm_style::Color::Red,
51 Color::Green => crossterm_style::Color::Green,
52 Color::Yellow => crossterm_style::Color::Yellow,
53 Color::Blue => crossterm_style::Color::Blue,
54 Color::Magenta => crossterm_style::Color::Magenta,
55 Color::Cyan => crossterm_style::Color::Cyan,
56 Color::White => crossterm_style::Color::White,
57 Color::BrightBlack => crossterm_style::Color::AnsiValue(8),
58 Color::BrightRed => crossterm_style::Color::AnsiValue(9),
59 Color::BrightGreen => crossterm_style::Color::AnsiValue(10),
60 Color::BrightYellow => crossterm_style::Color::AnsiValue(11),
61 Color::BrightBlue => crossterm_style::Color::AnsiValue(12),
62 Color::BrightMagenta => crossterm_style::Color::AnsiValue(13),
63 Color::BrightCyan => crossterm_style::Color::AnsiValue(14),
64 Color::BrightWhite => crossterm_style::Color::AnsiValue(15),
65 Color::Rgb(r, g, b) => crossterm_style::Color::Rgb { r, g, b },
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
72pub struct Modifier {
73 bits: u8,
74}
75
76impl Modifier {
77 pub const NONE: Self = Self { bits: 0 };
79 pub const BOLD: Self = Self { bits: 1 };
81 pub const ITALIC: Self = Self { bits: 2 };
83 pub const UNDERLINED: Self = Self { bits: 4 };
85 pub const REVERSED: Self = Self { bits: 8 };
87
88 pub fn contains(&self, other: Self) -> bool {
90 (self.bits & other.bits) == other.bits
91 }
92
93 pub fn insert(&mut self, other: Self) {
95 self.bits |= other.bits;
96 }
97
98 pub fn remove(&mut self, other: Self) {
100 self.bits &= !other.bits;
101 }
102}
103
104impl std::ops::BitOr for Modifier {
105 type Output = Self;
106
107 fn bitor(self, rhs: Self) -> Self {
108 Self { bits: self.bits | rhs.bits }
109 }
110}
111
112impl std::ops::BitOrAssign for Modifier {
113 fn bitor_assign(&mut self, rhs: Self) {
114 self.bits |= rhs.bits;
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct Style {
121 pub fg: Option<Color>,
123 pub bg: Option<Color>,
125 pub modifiers: Modifier,
127}
128
129impl Default for Style {
130 fn default() -> Self {
131 Self { fg: None, bg: None, modifiers: Modifier::NONE }
132 }
133}
134
135impl Style {
136 pub fn new() -> Self {
138 Self::default()
139 }
140
141 pub fn fg(mut self, color: Color) -> Self {
143 self.fg = Some(color);
144 self
145 }
146
147 pub fn bg(mut self, color: Color) -> Self {
149 self.bg = Some(color);
150 self
151 }
152
153 pub fn bold(mut self) -> Self {
155 self.modifiers.insert(Modifier::BOLD);
156 self
157 }
158
159 pub fn italic(mut self) -> Self {
161 self.modifiers.insert(Modifier::ITALIC);
162 self
163 }
164
165 pub fn underlined(mut self) -> Self {
167 self.modifiers.insert(Modifier::UNDERLINED);
168 self
169 }
170
171 pub fn reversed(mut self) -> Self {
173 self.modifiers.insert(Modifier::REVERSED);
174 self
175 }
176
177 pub fn combine(&mut self, other: &Style) {
179 if other.fg.is_some() {
180 self.fg = other.fg;
181 }
182 if other.bg.is_some() {
183 self.bg = other.bg;
184 }
185 self.modifiers |= other.modifiers;
186 }
187
188 pub fn to_crossterm_style(&self) -> crossterm_style::ContentStyle {
190 let mut content_style = crossterm_style::ContentStyle::default();
191
192 if let Some(fg) = self.fg {
194 content_style.foreground_color = Some(crossterm_style::Color::from(fg));
195 }
196
197 if let Some(bg) = self.bg {
198 content_style.background_color = Some(crossterm_style::Color::from(bg));
199 }
200
201 if self.modifiers.contains(Modifier::BOLD) {
203 content_style.attributes.set(crossterm_style::Attribute::Bold);
204 }
205
206 if self.modifiers.contains(Modifier::ITALIC) {
207 content_style.attributes.set(crossterm_style::Attribute::Italic);
208 }
209
210 if self.modifiers.contains(Modifier::UNDERLINED) {
211 content_style.attributes.set(crossterm_style::Attribute::Underlined);
212 }
213
214 if self.modifiers.contains(Modifier::REVERSED) {
215 content_style.attributes.set(crossterm_style::Attribute::Reverse);
216 }
217
218 content_style
219 }
220}