Skip to main content

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}