1use crate::color::Color;
4
5bitflags::bitflags! {
6 #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
21 pub struct CellModifier: u8 {
22 const BOLD = 1 << 0;
24 const DIM = 1 << 1;
26 const ITALIC = 1 << 2;
28 const UNDERLINE = 1 << 3;
30 const BLINK = 1 << 4;
32 const REVERSE = 1 << 5;
34 const HIDDEN = 1 << 6;
36 const STRIKETHROUGH = 1 << 7;
38 }
39}
40
41impl core::fmt::Debug for CellModifier {
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 if self.is_empty() {
46 return f.write_str("NONE");
47 }
48 let mut sep = "";
49 if self.contains(Self::BOLD) {
50 write!(f, "{sep}BOLD")?;
51 sep = " | ";
52 }
53 if self.contains(Self::DIM) {
54 write!(f, "{sep}DIM")?;
55 sep = " | ";
56 }
57 if self.contains(Self::ITALIC) {
58 write!(f, "{sep}ITALIC")?;
59 sep = " | ";
60 }
61 if self.contains(Self::UNDERLINE) {
62 write!(f, "{sep}UNDERLINE")?;
63 sep = " | ";
64 }
65 if self.contains(Self::BLINK) {
66 write!(f, "{sep}BLINK")?;
67 sep = " | ";
68 }
69 if self.contains(Self::REVERSE) {
70 write!(f, "{sep}REVERSE")?;
71 sep = " | ";
72 }
73 if self.contains(Self::HIDDEN) {
74 write!(f, "{sep}HIDDEN")?;
75 sep = " | ";
76 }
77 if self.contains(Self::STRIKETHROUGH) {
78 write!(f, "{sep}STRIKETHROUGH")?;
79 }
80 Ok(())
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
85pub struct Style {
87 pub(crate) fg: Color,
89 pub(crate) bg: Color,
91 pub(crate) modifiers: CellModifier,
93}
94
95impl Style {
96 #[must_use]
98 pub fn new() -> Self {
99 Self::default()
100 }
101
102 #[must_use]
104 pub const fn fg(mut self, color: Color) -> Self {
105 self.fg = color;
106 self
107 }
108
109 #[must_use]
111 pub const fn bg(mut self, color: Color) -> Self {
112 self.bg = color;
113 self
114 }
115
116 #[must_use]
118 pub fn bold(mut self) -> Self {
119 self.modifiers |= CellModifier::BOLD;
120 self
121 }
122
123 #[must_use]
125 pub fn italic(mut self) -> Self {
126 self.modifiers |= CellModifier::ITALIC;
127 self
128 }
129
130 #[must_use]
132 pub const fn foreground(&self) -> Color {
133 self.fg
134 }
135
136 #[must_use]
138 pub const fn background(&self) -> Color {
139 self.bg
140 }
141
142 #[must_use]
144 pub const fn modifiers(&self) -> CellModifier {
145 self.modifiers
146 }
147
148 #[must_use]
150 pub fn patch(mut self, other: Self) -> Self {
151 if other.fg != Color::Default {
152 self.fg = other.fg;
153 }
154 if other.bg != Color::Default {
155 self.bg = other.bg;
156 }
157 self.modifiers |= other.modifiers;
158 self
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_modifier_ops() {
168 let m = CellModifier::BOLD | CellModifier::ITALIC;
169 assert!(m.contains(CellModifier::BOLD));
170 assert!(m.contains(CellModifier::ITALIC));
171 assert!(!m.contains(CellModifier::UNDERLINE));
172 }
173
174 #[test]
175 fn test_modifier_debug() {
176 assert_eq!(format!("{:?}", CellModifier::empty()), "NONE");
177 assert_eq!(
178 format!("{:?}", CellModifier::BOLD | CellModifier::ITALIC),
179 "BOLD | ITALIC"
180 );
181 assert_eq!(
182 format!("{:?}", CellModifier::STRIKETHROUGH),
183 "STRIKETHROUGH"
184 );
185 }
186
187 #[test]
188 fn test_style_builder() {
189 let s = Style::new().fg(Color::RED).bold();
190 assert_eq!(s.foreground(), Color::RED);
191 assert!(s.modifiers().contains(CellModifier::BOLD));
192 }
193}