1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#[cfg(feature = "tty")]
use crossterm::style::{Attribute, Color};

use crate::style::CellAlignment;

/// A stylable table cell with content.
#[derive(Clone, Debug)]
pub struct Cell {
    /// The content is a list of strings.\
    /// This is done to make working with newlines more easily.\
    /// When creating a new [Cell], the given content is split by newline.
    pub(crate) content: Vec<String>,
    /// The delimiter which is used to split the text into consistent pieces.\
    /// The default is ` `.
    pub(crate) delimiter: Option<char>,
    pub(crate) alignment: Option<CellAlignment>,
    #[cfg(feature = "tty")]
    pub(crate) fg: Option<Color>,
    #[cfg(feature = "tty")]
    pub(crate) bg: Option<Color>,
    #[cfg(feature = "tty")]
    pub(crate) attributes: Vec<Attribute>,
}

impl Cell {
    /// Create a new Cell
    #[allow(clippy::needless_pass_by_value)]
    pub fn new<T: ToString>(content: T) -> Self {
        let content = content.to_string();
        #[cfg_attr(not(feature = "custom_styling"), allow(unused_mut))]
        let mut split_content: Vec<String> = content.split('\n').map(ToString::to_string).collect();

        // Correct ansi codes so style is terminated and resumed around the split
        #[cfg(feature = "custom_styling")]
        crate::utils::formatting::content_split::fix_style_in_split_str(&mut split_content);

        Self {
            content: split_content,
            delimiter: None,
            alignment: None,
            #[cfg(feature = "tty")]
            fg: None,
            #[cfg(feature = "tty")]
            bg: None,
            #[cfg(feature = "tty")]
            attributes: Vec::new(),
        }
    }

    /// Return a copy of the content contained in this cell.
    pub fn content(&self) -> String {
        self.content.join("\n")
    }

    /// Set the delimiter used to split text for this cell. \
    /// Normal text uses spaces (` `) as delimiters. This is necessary to help comfy-table
    /// understand the concept of _words_.
    #[must_use]
    pub fn set_delimiter(mut self, delimiter: char) -> Self {
        self.delimiter = Some(delimiter);

        self
    }

    /// Set the alignment of content for this cell.
    ///
    /// Setting this overwrites alignment settings of the
    /// [Column](crate::column::Column::set_cell_alignment) for this specific cell.
    /// ```
    /// use comfy_table::CellAlignment;
    /// use comfy_table::Cell;
    ///
    /// let mut cell = Cell::new("Some content")
    ///     .set_alignment(CellAlignment::Center);
    /// ```
    #[must_use]
    pub fn set_alignment(mut self, alignment: CellAlignment) -> Self {
        self.alignment = Some(alignment);

        self
    }

    /// Set the foreground text color for this cell.
    ///
    /// comfy-table uses [Crossterm Colors](crossterm::style::Color).
    /// Look at their documentation for all possible Colors.
    /// ```
    /// use comfy_table::Color;
    /// use comfy_table::Cell;
    ///
    /// let mut cell = Cell::new("Some content")
    ///     .fg(Color::Red);
    /// ```
    #[cfg(feature = "tty")]
    #[must_use]
    pub fn fg(mut self, color: Color) -> Self {
        self.fg = Some(color);

        self
    }

    /// Set the background color for this cell.
    ///
    /// comfy-table uses [Crossterm Colors](crossterm::style::Color).
    /// Look at their documentation for all possible Colors.
    /// ```
    /// use comfy_table::Color;
    /// use comfy_table::Cell;
    ///
    /// let mut cell = Cell::new("Some content")
    ///     .bg(Color::Red);
    /// ```
    #[cfg(feature = "tty")]
    #[must_use]
    pub fn bg(mut self, color: Color) -> Self {
        self.bg = Some(color);

        self
    }

    /// Add a styling attribute to the content cell.\
    /// Those can be **bold**, _italic_, blinking and many more.
    ///
    /// comfy-table uses [Crossterm Attributes](crossterm::style::Attribute).
    /// Look at their documentation for all possible [Attributes](Attribute).
    /// ```
    /// use comfy_table::Attribute;
    /// use comfy_table::Cell;
    ///
    /// let mut cell = Cell::new("Some content")
    ///     .add_attribute(Attribute::Bold);
    /// ```
    #[cfg(feature = "tty")]
    #[must_use]
    pub fn add_attribute(mut self, attribute: Attribute) -> Self {
        self.attributes.push(attribute);

        self
    }

    /// Same as add_attribute, but you can pass a vector of [Attributes](Attribute)
    #[cfg(feature = "tty")]
    #[must_use]
    pub fn add_attributes(mut self, mut attribute: Vec<Attribute>) -> Self {
        self.attributes.append(&mut attribute);

        self
    }
}

/// Convert anything with [ToString] to a new [Cell].
///
/// ```
/// # use comfy_table::Cell;
/// let cell: Cell = "content".into();
/// let cell: Cell = 5u32.into();
/// ```
impl<T: ToString> From<T> for Cell {
    fn from(content: T) -> Self {
        Self::new(content)
    }
}

/// A simple wrapper type for a `Vec<Cell>`.
///
/// This wrapper is needed to support generic conversions between iterables and `Vec<Cell>`.
/// Check the trait implementations for more docs.
pub struct Cells(pub Vec<Cell>);

/// Allow the conversion of a type to a [Cells], which is a simple vector of cells.
///
/// By default this is implemented for all Iterators over items implementing [ToString].
///
/// ```
/// use comfy_table::{Row, Cells};
///
/// let cells_string: Cells = vec!["One", "Two", "Three"].into();
/// let cells_integer: Cells = vec![1, 2, 3, 4].into();
/// ```
impl<T> From<T> for Cells
where
    T: IntoIterator,
    T::Item: Into<Cell>,
{
    fn from(cells: T) -> Self {
        Self(cells.into_iter().map(Into::into).collect())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_column_generation() {
        let content = "This is\nsome multiline\nstring".to_string();
        let cell = Cell::new(content.clone());

        assert_eq!(cell.content(), content);
    }
}