1use compact_str::CompactString;
2
3use crate::style::{Color, Modifier, Style};
4
5#[derive(Debug, Clone, Eq, PartialEq, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Cell {
9 symbol: CompactString,
18
19 pub fg: Color,
21
22 pub bg: Color,
24
25 #[cfg(feature = "underline-color")]
27 pub underline_color: Color,
28
29 pub modifier: Modifier,
31
32 pub skip: bool,
34}
35
36impl Cell {
37 pub const EMPTY: Self = Self::new(" ");
39
40 pub const fn new(symbol: &'static str) -> Self {
47 Self {
48 symbol: CompactString::const_new(symbol),
49 fg: Color::Reset,
50 bg: Color::Reset,
51 #[cfg(feature = "underline-color")]
52 underline_color: Color::Reset,
53 modifier: Modifier::empty(),
54 skip: false,
55 }
56 }
57
58 #[must_use]
60 pub fn symbol(&self) -> &str {
61 self.symbol.as_str()
62 }
63
64 pub fn set_symbol(&mut self, symbol: &str) -> &mut Self {
66 self.symbol = CompactString::new(symbol);
67 self
68 }
69
70 pub(crate) fn append_symbol(&mut self, symbol: &str) -> &mut Self {
74 self.symbol.push_str(symbol);
75 self
76 }
77
78 pub fn set_char(&mut self, ch: char) -> &mut Self {
80 let mut buf = [0; 4];
81 self.symbol = CompactString::new(ch.encode_utf8(&mut buf));
82 self
83 }
84
85 pub fn set_fg(&mut self, color: Color) -> &mut Self {
87 self.fg = color;
88 self
89 }
90
91 pub fn set_bg(&mut self, color: Color) -> &mut Self {
93 self.bg = color;
94 self
95 }
96
97 pub fn set_style<S: Into<Style>>(&mut self, style: S) -> &mut Self {
102 let style = style.into();
103 if let Some(c) = style.fg {
104 self.fg = c;
105 }
106 if let Some(c) = style.bg {
107 self.bg = c;
108 }
109 #[cfg(feature = "underline-color")]
110 if let Some(c) = style.underline_color {
111 self.underline_color = c;
112 }
113 self.modifier.insert(style.add_modifier);
114 self.modifier.remove(style.sub_modifier);
115 self
116 }
117
118 #[must_use]
120 pub const fn style(&self) -> Style {
121 Style {
122 fg: Some(self.fg),
123 bg: Some(self.bg),
124 #[cfg(feature = "underline-color")]
125 underline_color: Some(self.underline_color),
126 add_modifier: self.modifier,
127 sub_modifier: Modifier::empty(),
128 }
129 }
130
131 pub fn set_skip(&mut self, skip: bool) -> &mut Self {
136 self.skip = skip;
137 self
138 }
139
140 pub fn reset(&mut self) {
142 self.symbol = CompactString::const_new(" ");
143 self.fg = Color::Reset;
144 self.bg = Color::Reset;
145 #[cfg(feature = "underline-color")]
146 {
147 self.underline_color = Color::Reset;
148 }
149 self.modifier = Modifier::empty();
150 self.skip = false;
151 }
152}
153
154impl Default for Cell {
155 fn default() -> Self {
156 Self::EMPTY
157 }
158}
159
160impl From<char> for Cell {
161 fn from(ch: char) -> Self {
162 let mut cell = Self::EMPTY;
163 cell.set_char(ch);
164 cell
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn new() {
174 let cell = Cell::new("ใ");
175 assert_eq!(
176 cell,
177 Cell {
178 symbol: CompactString::const_new("ใ"),
179 fg: Color::Reset,
180 bg: Color::Reset,
181 #[cfg(feature = "underline-color")]
182 underline_color: Color::Reset,
183 modifier: Modifier::empty(),
184 skip: false,
185 }
186 );
187 }
188
189 #[test]
190 fn empty() {
191 let cell = Cell::EMPTY;
192 assert_eq!(cell.symbol(), " ");
193 }
194
195 #[test]
196 fn set_symbol() {
197 let mut cell = Cell::EMPTY;
198 cell.set_symbol("ใ"); assert_eq!(cell.symbol(), "ใ");
200 cell.set_symbol("๐จโ๐ฉโ๐งโ๐ฆ"); assert_eq!(cell.symbol(), "๐จโ๐ฉโ๐งโ๐ฆ");
202 }
203
204 #[test]
205 fn append_symbol() {
206 let mut cell = Cell::EMPTY;
207 cell.set_symbol("ใ"); cell.append_symbol("\u{200B}"); assert_eq!(cell.symbol(), "ใ\u{200B}");
210 }
211
212 #[test]
213 fn set_char() {
214 let mut cell = Cell::EMPTY;
215 cell.set_char('ใ'); assert_eq!(cell.symbol(), "ใ");
217 }
218
219 #[test]
220 fn set_fg() {
221 let mut cell = Cell::EMPTY;
222 cell.set_fg(Color::Red);
223 assert_eq!(cell.fg, Color::Red);
224 }
225
226 #[test]
227 fn set_bg() {
228 let mut cell = Cell::EMPTY;
229 cell.set_bg(Color::Red);
230 assert_eq!(cell.bg, Color::Red);
231 }
232
233 #[test]
234 fn set_style() {
235 let mut cell = Cell::EMPTY;
236 cell.set_style(Style::new().fg(Color::Red).bg(Color::Blue));
237 assert_eq!(cell.fg, Color::Red);
238 assert_eq!(cell.bg, Color::Blue);
239 }
240
241 #[test]
242 fn set_skip() {
243 let mut cell = Cell::EMPTY;
244 cell.set_skip(true);
245 assert!(cell.skip);
246 }
247
248 #[test]
249 fn reset() {
250 let mut cell = Cell::EMPTY;
251 cell.set_symbol("ใ");
252 cell.set_fg(Color::Red);
253 cell.set_bg(Color::Blue);
254 cell.set_skip(true);
255 cell.reset();
256 assert_eq!(cell.symbol(), " ");
257 assert_eq!(cell.fg, Color::Reset);
258 assert_eq!(cell.bg, Color::Reset);
259 assert!(!cell.skip);
260 }
261
262 #[test]
263 fn style() {
264 let cell = Cell::EMPTY;
265 assert_eq!(
266 cell.style(),
267 Style {
268 fg: Some(Color::Reset),
269 bg: Some(Color::Reset),
270 #[cfg(feature = "underline-color")]
271 underline_color: Some(Color::Reset),
272 add_modifier: Modifier::empty(),
273 sub_modifier: Modifier::empty(),
274 }
275 );
276 }
277
278 #[test]
279 fn default() {
280 let cell = Cell::default();
281 assert_eq!(cell.symbol(), " ");
282 }
283
284 #[test]
285 fn cell_eq() {
286 let cell1 = Cell::new("ใ");
287 let cell2 = Cell::new("ใ");
288 assert_eq!(cell1, cell2);
289 }
290
291 #[test]
292 fn cell_ne() {
293 let cell1 = Cell::new("ใ");
294 let cell2 = Cell::new("ใ");
295 assert_ne!(cell1, cell2);
296 }
297}