slack_messaging/blocks/rich_text/list.rs
1use super::RichTextSection;
2use crate::validators::*;
3
4use serde::Serialize;
5use slack_messaging_derive::Builder;
6
7/// [Rich text list element](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block#rich_text_list)
8/// representation.
9///
10/// # Fields and Validations
11///
12/// For more details, see the [official
13/// documentation](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block#rich_text_list).
14///
15/// | Field | Type | Required | Validation |
16/// |-------|------|----------|------------|
17/// | style | [ListStyle] | Yes | N/A |
18/// | elements | Vec<[RichTextSection]> | Yes | N/A |
19/// | indent | i64 | No | N/A |
20/// | offset | i64 | No | N/A |
21/// | border | i64 | No | N/A |
22///
23/// # Example
24///
25/// ```
26/// use slack_messaging::blocks::rich_text::prelude::*;
27/// # use std::error::Error;
28///
29/// # fn try_main() -> Result<(), Box<dyn Error>> {
30/// let list = RichTextList::builder()
31/// .element(
32/// RichTextSection::builder()
33/// .element(
34/// RichTextElementText::builder()
35/// .text("Huddles")
36/// .build()?
37/// )
38/// .build()?
39/// )
40/// .element(
41/// RichTextSection::builder()
42/// .element(
43/// RichTextElementText::builder()
44/// .text("Canvas")
45/// .build()?
46/// )
47/// .build()?
48/// )
49/// .style(ListStyle::Bullet)
50/// .indent(0)
51/// .border(1)
52/// .build()?;
53///
54/// let expected = serde_json::json!({
55/// "type": "rich_text_list",
56/// "elements": [
57/// {
58/// "type": "rich_text_section",
59/// "elements": [
60/// {
61/// "type": "text",
62/// "text": "Huddles",
63/// }
64/// ]
65/// },
66/// {
67/// "type": "rich_text_section",
68/// "elements": [
69/// {
70/// "type": "text",
71/// "text": "Canvas",
72/// }
73/// ]
74/// }
75/// ],
76/// "style": "bullet",
77/// "indent": 0,
78/// "border": 1
79/// });
80///
81/// let json = serde_json::to_value(list).unwrap();
82///
83/// assert_eq!(json, expected);
84///
85/// // If your object has any validation errors, the build method returns Result::Err
86/// let list = RichTextList::builder().build();
87/// assert!(list.is_err());
88/// # Ok(())
89/// # }
90/// # fn main() {
91/// # try_main().unwrap()
92/// # }
93/// ```
94#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
95#[serde(tag = "type", rename = "rich_text_list")]
96pub struct RichTextList {
97 #[builder(validate("required"))]
98 pub(crate) style: Option<ListStyle>,
99
100 #[builder(push_item = "element", validate("required"))]
101 pub(crate) elements: Option<Vec<RichTextSection>>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub(crate) indent: Option<i64>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub(crate) offset: Option<i64>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub(crate) border: Option<i64>,
111}
112
113/// List style for [`RichTextList`].
114#[derive(Debug, Clone, Serialize, PartialEq)]
115#[serde(rename_all = "snake_case")]
116pub enum ListStyle {
117 /// Bullet list style.
118 Bullet,
119 /// Ordered list style.
120 Ordered,
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::blocks::rich_text::{test_helpers::*, types::test_helpers::*};
127 use crate::errors::*;
128
129 #[test]
130 fn it_implements_builder() {
131 let expected = RichTextList {
132 style: Some(ListStyle::Bullet),
133 elements: Some(vec![
134 section(vec![el_text("foo")]),
135 section(vec![el_text("bar")]),
136 ]),
137 indent: Some(1),
138 offset: Some(2),
139 border: Some(3),
140 };
141
142 let val = RichTextList::builder()
143 .set_style(Some(ListStyle::Bullet))
144 .set_elements(Some(vec![
145 section(vec![el_text("foo")]),
146 section(vec![el_text("bar")]),
147 ]))
148 .set_indent(Some(1))
149 .set_offset(Some(2))
150 .set_border(Some(3))
151 .build()
152 .unwrap();
153
154 assert_eq!(val, expected);
155
156 let val = RichTextList::builder()
157 .style(ListStyle::Bullet)
158 .elements(vec![
159 section(vec![el_text("foo")]),
160 section(vec![el_text("bar")]),
161 ])
162 .indent(1)
163 .offset(2)
164 .border(3)
165 .build()
166 .unwrap();
167
168 assert_eq!(val, expected);
169 }
170
171 #[test]
172 fn it_implements_push_item_method() {
173 let expected = RichTextList {
174 style: Some(ListStyle::Ordered),
175 elements: Some(vec![
176 section(vec![el_text("foo")]),
177 section(vec![el_text("bar")]),
178 ]),
179 indent: None,
180 offset: None,
181 border: None,
182 };
183
184 let val = RichTextList::builder()
185 .style(ListStyle::Ordered)
186 .element(section(vec![el_text("foo")]))
187 .element(section(vec![el_text("bar")]))
188 .build()
189 .unwrap();
190
191 assert_eq!(val, expected);
192 }
193
194 #[test]
195 fn it_requires_style_field() {
196 let err = RichTextList::builder()
197 .element(section(vec![el_text("foo")]))
198 .build()
199 .unwrap_err();
200 assert_eq!(err.object(), "RichTextList");
201
202 let errors = err.field("style");
203 assert!(errors.includes(ValidationErrorKind::Required));
204 }
205
206 #[test]
207 fn it_requires_elements_field() {
208 let err = RichTextList::builder()
209 .style(ListStyle::Ordered)
210 .build()
211 .unwrap_err();
212 assert_eq!(err.object(), "RichTextList");
213
214 let errors = err.field("elements");
215 assert!(errors.includes(ValidationErrorKind::Required));
216 }
217}