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}