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}