super_table/
cell.rs

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