Skip to main content

slack_messaging/blocks/data_table/
row.rs

1use super::DataTableCell;
2
3use crate::validators::*;
4
5use serde::Serialize;
6use slack_messaging_derive::Builder;
7
8/// Single table row representation being set to the rows field in [`DataTable`](crate::blocks::DataTable)
9/// object.
10///
11/// Table rows contain an array of table cells, each represented by
12/// [`DataTableCell`] enum.
13///
14/// # Fields and Validations
15///
16/// | Field | Type | Required | Validation |
17/// |-------|------|----------|------------|
18/// | cells | Vec<[DataTableCell]> | Yes | Maximum of 20 items. |
19///
20/// # Example
21///
22/// ```
23/// use slack_messaging::blocks::RichText;
24/// use slack_messaging::blocks::rich_text::RichTextSection;
25/// use slack_messaging::blocks::rich_text::types::{RichTextElementText, RichTextStyle};
26/// use slack_messaging::blocks::data_table::DataTableRow;
27/// # use std::error::Error;
28///
29/// # fn try_main() -> Result<(), Box<dyn Error>> {
30/// let row = DataTableRow::builder()
31///     .cell("Data Refinement Department")
32///     .cell("MDR")
33///     .cell(
34///         RichText::builder()
35///             .element(
36///                 RichTextSection::builder()
37///                     .element(
38///                         RichTextElementText::builder()
39///                             .text("Blue")
40///                             .style(
41///                                 RichTextStyle::builder()
42///                                     .bold(true)
43///                                     .build()?
44///                             )
45///                             .build()?
46///                     )
47///                     .build()?
48///             )
49///             .build()?
50///     )
51///     .build()?;
52///
53/// let expected = serde_json::json!([
54///     {
55///         "type": "raw_text",
56///         "text": "Data Refinement Department"
57///     },
58///     {
59///         "type": "raw_text",
60///         "text": "MDR"
61///     },
62///     {
63///         "type": "rich_text",
64///         "elements": [
65///             {
66///                 "type": "rich_text_section",
67///                 "elements": [
68///                     {
69///                         "type": "text",
70///                         "text": "Blue",
71///                         "style": {
72///                             "bold": true
73///                         }
74///                     }
75///                 ]
76///             }
77///         ]
78///     }
79/// ]);
80///
81/// let json = serde_json::to_value(row).unwrap();
82///
83/// assert_eq!(json, expected);
84/// #     Ok(())
85/// # }
86/// # fn main() {
87/// #     try_main().unwrap()
88/// # }
89/// ```
90#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
91#[serde(transparent)]
92pub struct DataTableRow {
93    #[builder(push_item = "cell", validate("required", "list::max_item_20"))]
94    pub(crate) cells: Option<Vec<DataTableCell>>,
95}
96
97impl<Cell: Into<DataTableCell>> FromIterator<Cell> for DataTableRow {
98    fn from_iter<T: IntoIterator<Item = Cell>>(iter: T) -> Self {
99        Self {
100            cells: Some(iter.into_iter().map(|c| c.into()).collect()),
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use crate::blocks::test_helpers::*;
109    use crate::blocks::data_table::RawNumber;
110    use crate::errors::*;
111
112    #[test]
113    fn it_implements_builder() {
114        let expected = DataTableRow {
115            cells: Some(vec![
116                DataTableCell::RawText("foo".into()),
117                DataTableCell::RawNumber((10, "foobar").into()),
118                DataTableCell::RichText(rich_text()),
119            ]),
120        };
121
122        let val = DataTableRow::builder()
123            .set_cells(Some(vec![
124                DataTableCell::RawText("foo".into()),
125                DataTableCell::RawNumber((10, "foobar").into()),
126                DataTableCell::RichText(rich_text()),
127            ]))
128            .build()
129            .unwrap();
130
131        assert_eq!(val, expected);
132
133        let val = DataTableRow::builder()
134            .cells(vec![
135                DataTableCell::RawText("foo".into()),
136                DataTableCell::RawNumber((10, "foobar").into()),
137                DataTableCell::RichText(rich_text()),
138            ])
139            .build()
140            .unwrap();
141
142        assert_eq!(val, expected);
143    }
144
145    #[test]
146    fn it_implements_push_item_method() {
147        let expected = DataTableRow {
148            cells: Some(vec![
149                DataTableCell::RawText("foo".into()),
150                DataTableCell::RawNumber((10, "foobar").into()),
151                DataTableCell::RichText(rich_text()),
152            ]),
153        };
154
155        let val = DataTableRow::builder()
156            .cell("foo")
157            .cell(RawNumber::new(10, "foobar"))
158            .cell(rich_text())
159            .build()
160            .unwrap();
161
162        assert_eq!(val, expected);
163    }
164
165    #[test]
166    fn it_requires_cells_field() {
167        let err = DataTableRow::builder().build().unwrap_err();
168        assert_eq!(err.object(), "DataTableRow");
169
170        let errors = err.field("cells");
171        assert!(errors.includes(ValidationErrorKind::Required));
172    }
173
174    #[test]
175    fn it_requires_cells_field_to_have_max_20_items() {
176        let cells: Vec<DataTableCell> = (0..21).map(|_| "foo".into()).collect();
177        let err = DataTableRow::builder().cells(cells).build().unwrap_err();
178        assert_eq!(err.object(), "DataTableRow");
179
180        let errors = err.field("cells");
181        assert!(errors.includes(ValidationErrorKind::MaxArraySize(20)));
182    }
183}