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