super_table/cell.rs
1#[cfg(feature = "tty")]
2use crate::{Attribute, Color};
3
4use crate::style::{CellAlignment, VerticalAlignment};
5
6/// A stylable table cell with content.
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8pub struct Cell {
9 /// The content is a list of strings.\
10 /// This is done to make working with newlines more easily.\
11 /// When creating a new [Cell], the given content is split by newline.
12 pub(crate) content: Vec<String>,
13 /// The delimiter which is used to split the text into consistent pieces.\
14 /// The default is ` `.
15 pub(crate) delimiter: Option<char>,
16 pub(crate) alignment: Option<CellAlignment>,
17 pub(crate) vertical_alignment: Option<VerticalAlignment>,
18 #[cfg(feature = "tty")]
19 pub(crate) fg: Option<Color>,
20 #[cfg(feature = "tty")]
21 pub(crate) bg: Option<Color>,
22 #[cfg(feature = "tty")]
23 pub(crate) attributes: Vec<Attribute>,
24 /// Number of columns this cell spans (default: 1)
25 pub(crate) colspan: Option<u16>,
26 /// Number of rows this cell spans (default: 1)
27 pub(crate) rowspan: Option<u16>,
28}
29
30impl Cell {
31 /// Create a new Cell
32 #[allow(clippy::needless_pass_by_value)]
33 pub fn new<T: ToString>(content: T) -> Self {
34 Self::new_owned(content.to_string())
35 }
36
37 /// Create a new Cell from an owned String
38 pub fn new_owned(content: String) -> Self {
39 #[cfg_attr(not(feature = "custom_styling"), allow(unused_mut))]
40 let mut split_content: Vec<String> = content.split('\n').map(ToString::to_string).collect();
41
42 // Correct ansi codes so style is terminated and resumed around the split
43 #[cfg(feature = "custom_styling")]
44 crate::utils::formatting::content_split::fix_style_in_split_str(&mut split_content);
45
46 Self {
47 content: split_content,
48 delimiter: None,
49 alignment: None,
50 vertical_alignment: None,
51 #[cfg(feature = "tty")]
52 fg: None,
53 #[cfg(feature = "tty")]
54 bg: None,
55 #[cfg(feature = "tty")]
56 attributes: Vec::new(),
57 colspan: None,
58 rowspan: None,
59 }
60 }
61
62 /// Return a copy of the content contained in this cell.
63 pub fn content(&self) -> String {
64 self.content.join("\n")
65 }
66
67 /// Set the delimiter used to split text for this cell. \
68 /// Normal text uses spaces (` `) as delimiters. This is necessary to help super-table
69 /// understand the concept of _words_.
70 #[must_use]
71 pub fn set_delimiter(mut self, delimiter: char) -> Self {
72 self.delimiter = Some(delimiter);
73
74 self
75 }
76
77 /// Set the horizontal alignment of content for this cell.
78 ///
79 /// Setting this overwrites alignment settings of the
80 /// [Column](crate::column::Column::set_cell_alignment) for this specific cell.
81 /// ```
82 /// use super_table::CellAlignment;
83 /// use super_table::Cell;
84 ///
85 /// let mut cell = Cell::new("Some content")
86 /// .set_alignment(CellAlignment::Center);
87 /// ```
88 #[must_use]
89 pub fn set_alignment(mut self, alignment: CellAlignment) -> Self {
90 self.alignment = Some(alignment);
91
92 self
93 }
94
95 /// Set the vertical alignment of content for this cell.
96 ///
97 /// This controls where the content is positioned vertically when the cell's
98 /// row has more lines than this cell's content (e.g., due to another cell
99 /// in the same row having multi-line content).
100 ///
101 /// Setting this overwrites vertical alignment settings of the
102 /// [Column](crate::column::Column::set_vertical_alignment) for this specific cell.
103 /// ```
104 /// use super_table::VerticalAlignment;
105 /// use super_table::Cell;
106 ///
107 /// let mut cell = Cell::new("Some content")
108 /// .set_vertical_alignment(VerticalAlignment::Middle);
109 /// ```
110 #[must_use]
111 pub fn set_vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
112 self.vertical_alignment = Some(alignment);
113
114 self
115 }
116
117 /// Set the foreground text color for this cell.
118 ///
119 /// Look at [Color](crate::Color) for a list of all possible Colors.
120 /// ```
121 /// use super_table::Color;
122 /// use super_table::Cell;
123 ///
124 /// let mut cell = Cell::new("Some content")
125 /// .fg(Color::Red);
126 /// ```
127 #[cfg(feature = "tty")]
128 #[must_use]
129 pub fn fg(mut self, color: Color) -> Self {
130 self.fg = Some(color);
131
132 self
133 }
134
135 /// Set the background color for this cell.
136 ///
137 /// Look at [Color](crate::Color) for a list of all possible Colors.
138 /// ```
139 /// use super_table::Color;
140 /// use super_table::Cell;
141 ///
142 /// let mut cell = Cell::new("Some content")
143 /// .bg(Color::Red);
144 /// ```
145 #[cfg(feature = "tty")]
146 #[must_use]
147 pub fn bg(mut self, color: Color) -> Self {
148 self.bg = Some(color);
149
150 self
151 }
152
153 /// Add a styling attribute to the content cell.\
154 /// Those can be **bold**, _italic_, blinking and many more.
155 ///
156 /// Look at [Attribute](crate::Attribute) for a list of all possible Colors.
157 /// ```
158 /// use super_table::Attribute;
159 /// use super_table::Cell;
160 ///
161 /// let mut cell = Cell::new("Some content")
162 /// .add_attribute(Attribute::Bold);
163 /// ```
164 #[cfg(feature = "tty")]
165 #[must_use]
166 pub fn add_attribute(mut self, attribute: Attribute) -> Self {
167 self.attributes.push(attribute);
168
169 self
170 }
171
172 /// Same as add_attribute, but you can pass a vector of [Attributes](Attribute)
173 #[cfg(feature = "tty")]
174 #[must_use]
175 pub fn add_attributes(mut self, mut attribute: Vec<Attribute>) -> Self {
176 self.attributes.append(&mut attribute);
177
178 self
179 }
180
181 /// Set the number of columns this cell spans.
182 ///
183 /// By default, a cell spans 1 column. Setting a colspan greater than 1
184 /// makes the cell occupy multiple columns. The cell's content will be
185 /// rendered across all spanned columns, and borders between the spanned
186 /// columns will be omitted.
187 ///
188 /// # Examples
189 ///
190 /// ```
191 /// use super_table::{Cell, Table};
192 ///
193 /// let mut table = Table::new();
194 /// table
195 /// .set_header(vec![
196 /// Cell::new("Header1").set_colspan(2),
197 /// Cell::new("Header3"),
198 /// ])
199 /// .add_row(vec![
200 /// Cell::new("Spans 2 columns").set_colspan(2),
201 /// Cell::new("Normal cell"),
202 /// ]);
203 /// ```
204 ///
205 /// # Notes
206 ///
207 /// - When using colspan, you should add fewer cells to the row than the
208 /// number of columns. The spanned cell counts as multiple columns.
209 /// - Colspan works with all table features including styling, alignment,
210 /// and dynamic width arrangement.
211 /// - Hidden columns are automatically excluded from colspan calculations.
212 #[must_use]
213 pub fn set_colspan(mut self, cols: u16) -> Self {
214 self.colspan = Some(cols);
215 self
216 }
217
218 /// Set the number of rows this cell spans.
219 ///
220 /// By default, a cell spans 1 row. Setting a rowspan greater than 1
221 /// makes the cell occupy multiple rows. The cell's content will appear
222 /// only in the first row of the span, and subsequent rows will have
223 /// empty space where the rowspan cell is located.
224 ///
225 /// # Examples
226 ///
227 /// ```
228 /// use super_table::{Cell, Table};
229 ///
230 /// let mut table = Table::new();
231 /// table
232 /// .set_header(vec!["Header1", "Header2", "Header3"])
233 /// .add_row(vec![
234 /// Cell::new("Spans 2 rows").set_rowspan(2),
235 /// Cell::new("Cell 2"),
236 /// Cell::new("Cell 3"),
237 /// ])
238 /// .add_row(vec![
239 /// // First position is occupied by rowspan above, so only add 2 cells
240 /// Cell::new("Cell 2 (row 2)"),
241 /// Cell::new("Cell 3 (row 2)"),
242 /// ]);
243 /// ```
244 ///
245 /// # Notes
246 ///
247 /// - When using rowspan, subsequent rows should have fewer cells than
248 /// the number of columns, as the rowspan cell occupies space in those rows.
249 /// - Rowspan content appears only in the starting row of the span.
250 /// - Rowspan works with all table features including styling, alignment,
251 /// and multi-line content.
252 /// - You can combine rowspan with colspan to create cells that span
253 /// both multiple rows and columns.
254 #[must_use]
255 pub fn set_rowspan(mut self, rows: u16) -> Self {
256 self.rowspan = Some(rows);
257 self
258 }
259
260 /// Get the number of columns this cell spans.
261 ///
262 /// Returns 1 if no colspan is set (default behavior).
263 ///
264 /// ```
265 /// use super_table::Cell;
266 ///
267 /// let cell = Cell::new("Content");
268 /// assert_eq!(cell.colspan(), 1);
269 ///
270 /// let cell = Cell::new("Content").set_colspan(3);
271 /// assert_eq!(cell.colspan(), 3);
272 /// ```
273 pub fn colspan(&self) -> u16 {
274 self.colspan.unwrap_or(1)
275 }
276
277 /// Get the number of rows this cell spans.
278 ///
279 /// Returns 1 if no rowspan is set (default behavior).
280 ///
281 /// ```
282 /// use super_table::Cell;
283 ///
284 /// let cell = Cell::new("Content");
285 /// assert_eq!(cell.rowspan(), 1);
286 ///
287 /// let cell = Cell::new("Content").set_rowspan(2);
288 /// assert_eq!(cell.rowspan(), 2);
289 /// ```
290 pub fn rowspan(&self) -> u16 {
291 self.rowspan.unwrap_or(1)
292 }
293
294 /// Alias for [set_colspan](Cell::set_colspan).
295 ///
296 /// ```
297 /// use super_table::Cell;
298 ///
299 /// let cell = Cell::new("Spans 2 columns")
300 /// .span_columns(2);
301 /// ```
302 #[must_use]
303 pub fn span_columns(self, cols: u16) -> Self {
304 self.set_colspan(cols)
305 }
306
307 /// Alias for [set_rowspan](Cell::set_rowspan).
308 ///
309 /// ```
310 /// use super_table::Cell;
311 ///
312 /// let cell = Cell::new("Spans 2 rows")
313 /// .span_rows(2);
314 /// ```
315 #[must_use]
316 pub fn span_rows(self, rows: u16) -> Self {
317 self.set_rowspan(rows)
318 }
319}
320
321/// Convert anything with [ToString] to a new [Cell].
322///
323/// ```
324/// # use super_table::Cell;
325/// let cell: Cell = "content".into();
326/// let cell: Cell = 5u32.into();
327/// ```
328impl<T: ToString> From<T> for Cell {
329 fn from(content: T) -> Self {
330 Self::new(content)
331 }
332}
333
334/// A simple wrapper type for a `Vec<Cell>`.
335///
336/// This wrapper is needed to support generic conversions between iterables and `Vec<Cell>`.
337/// Check the trait implementations for more docs.
338pub struct Cells(pub Vec<Cell>);
339
340/// Allow the conversion of a type to a [Cells], which is a simple vector of cells.
341///
342/// By default this is implemented for all Iterators over items implementing [ToString].
343///
344/// ```
345/// use super_table::{Row, Cells};
346///
347/// let cells_string: Cells = vec!["One", "Two", "Three"].into();
348/// let cells_integer: Cells = vec![1, 2, 3, 4].into();
349/// ```
350impl<T> From<T> for Cells
351where
352 T: IntoIterator,
353 T::Item: Into<Cell>,
354{
355 fn from(cells: T) -> Self {
356 Self(cells.into_iter().map(Into::into).collect())
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn test_column_generation() {
366 let content = "This is\nsome multiline\nstring".to_string();
367 let cell = Cell::new(content.clone());
368
369 assert_eq!(cell.content(), content);
370 }
371}