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