1use rsb_derive::Builder;
2use rvstruct::ValueStruct;
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5use url::Url;
6
7use super::workflow::SlackBlockWorkflowButtonElement;
8use crate::*;
9
10#[skip_serializing_none]
11#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
12pub struct SlackBlockId(pub String);
13
14#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
15pub struct SlackTaskId(pub String);
16
17#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
18pub struct SlackAccessibilityLabel(pub String);
19
20#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
21#[serde(tag = "type")]
22pub enum SlackBlock {
23 #[serde(rename = "section")]
24 Section(SlackSectionBlock),
25 #[serde(rename = "header")]
26 Header(SlackHeaderBlock),
27 #[serde(rename = "divider")]
28 Divider(SlackDividerBlock),
29 #[serde(rename = "image")]
30 Image(SlackImageBlock),
31 #[serde(rename = "actions")]
32 Actions(SlackActionsBlock),
33 #[serde(rename = "context")]
34 Context(SlackContextBlock),
35 #[serde(rename = "input")]
36 Input(SlackInputBlock),
37 #[serde(rename = "file")]
38 File(SlackFileBlock),
39 #[serde(rename = "video")]
40 Video(SlackVideoBlock),
41 #[serde(rename = "markdown")]
42 Markdown(SlackMarkdownBlock),
43 #[serde(rename = "rich_text")]
44 RichText(SlackRichTextBlock),
45 #[serde(rename = "table")]
46 Table(SlackTableBlock),
47 #[serde(rename = "task_card")]
48 TaskCard(SlackTaskCardBlock),
49 #[serde(rename = "alert")]
50 Alert(SlackAlertBlock),
51 #[serde(rename = "card")]
52 Card(SlackCardBlock),
53 #[serde(rename = "carousel")]
54 Carousel(SlackCarouselBlock),
55 #[serde(rename = "context_actions")]
56 ContextActions(SlackContextActionsBlock),
57 #[serde(rename = "share_shortcut")]
58 ShareShortcut(serde_json::Value),
59 #[serde(rename = "event")]
60 Event(serde_json::Value),
61}
62
63#[skip_serializing_none]
64#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
65pub struct SlackSectionBlock {
66 pub block_id: Option<SlackBlockId>,
67 pub text: Option<SlackBlockText>,
68 pub fields: Option<Vec<SlackBlockText>>,
69 pub accessory: Option<SlackSectionBlockElement>,
70 pub expand: Option<bool>,
71}
72
73impl From<SlackSectionBlock> for SlackBlock {
74 fn from(block: SlackSectionBlock) -> Self {
75 SlackBlock::Section(block)
76 }
77}
78
79#[skip_serializing_none]
80#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
81pub struct SlackHeaderBlock {
82 pub block_id: Option<SlackBlockId>,
83 pub text: SlackBlockPlainTextOnly,
84}
85
86impl From<SlackHeaderBlock> for SlackBlock {
87 fn from(block: SlackHeaderBlock) -> Self {
88 SlackBlock::Header(block)
89 }
90}
91
92#[skip_serializing_none]
93#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
94pub struct SlackDividerBlock {
95 pub block_id: Option<SlackBlockId>,
96}
97
98impl From<SlackDividerBlock> for SlackBlock {
99 fn from(block: SlackDividerBlock) -> Self {
100 SlackBlock::Divider(block)
101 }
102}
103
104#[skip_serializing_none]
105#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
106pub struct SlackImageBlock {
107 pub block_id: Option<SlackBlockId>,
108 #[serde(flatten)]
109 pub image_url_or_file: SlackImageUrlOrFile,
110 pub alt_text: String,
111 pub title: Option<SlackBlockPlainTextOnly>,
112}
113
114impl From<SlackImageBlock> for SlackBlock {
115 fn from(block: SlackImageBlock) -> Self {
116 SlackBlock::Image(block)
117 }
118}
119
120#[skip_serializing_none]
121#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
122pub struct SlackActionsBlock {
123 pub block_id: Option<SlackBlockId>,
124 pub elements: Vec<SlackActionBlockElement>,
125}
126
127impl From<SlackActionsBlock> for SlackBlock {
128 fn from(block: SlackActionsBlock) -> Self {
129 SlackBlock::Actions(block)
130 }
131}
132
133#[skip_serializing_none]
134#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
135pub struct SlackContextBlock {
136 pub block_id: Option<SlackBlockId>,
137 pub elements: Vec<SlackContextBlockElement>,
138}
139
140impl From<SlackContextBlock> for SlackBlock {
141 fn from(block: SlackContextBlock) -> Self {
142 SlackBlock::Context(block)
143 }
144}
145
146#[skip_serializing_none]
147#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
148pub struct SlackInputBlock {
149 pub block_id: Option<SlackBlockId>,
150 pub label: SlackBlockPlainTextOnly,
151 pub element: SlackInputBlockElement,
152 pub hint: Option<SlackBlockPlainTextOnly>,
153 pub optional: Option<bool>,
154 pub dispatch_action: Option<bool>,
155}
156
157impl From<SlackInputBlock> for SlackBlock {
158 fn from(block: SlackInputBlock) -> Self {
159 SlackBlock::Input(block)
160 }
161}
162
163const SLACK_FILE_BLOCK_SOURCE_DEFAULT: &str = "remote";
164
165#[skip_serializing_none]
166#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
167pub struct SlackFileBlock {
168 pub block_id: Option<SlackBlockId>,
169 pub external_id: String,
170 #[default = "SLACK_FILE_BLOCK_SOURCE_DEFAULT.into()"]
171 pub source: String,
172}
173
174impl From<SlackFileBlock> for SlackBlock {
175 fn from(block: SlackFileBlock) -> Self {
176 SlackBlock::File(block)
177 }
178}
179
180#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
181#[serde(tag = "type")]
182pub enum SlackSectionBlockElement {
183 #[serde(rename = "image")]
184 Image(SlackBlockImageElement),
185 #[serde(rename = "button")]
186 Button(SlackBlockButtonElement),
187 #[serde(rename = "static_select")]
188 StaticSelect(SlackBlockStaticSelectElement),
189 #[serde(rename = "multi_static_select")]
190 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
191 #[serde(rename = "external_select")]
192 ExternalSelect(SlackBlockExternalSelectElement),
193 #[serde(rename = "multi_external_select")]
194 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
195 #[serde(rename = "users_select")]
196 UsersSelect(SlackBlockUsersSelectElement),
197 #[serde(rename = "multi_users_select")]
198 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
199 #[serde(rename = "conversations_select")]
200 ConversationsSelect(SlackBlockConversationsSelectElement),
201 #[serde(rename = "multi_conversations_select")]
202 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
203 #[serde(rename = "channels_select")]
204 ChannelsSelect(SlackBlockChannelsSelectElement),
205 #[serde(rename = "multi_channels_select")]
206 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
207 #[serde(rename = "overflow")]
208 Overflow(SlackBlockOverflowElement),
209 #[serde(rename = "datepicker")]
210 DatePicker(SlackBlockDatePickerElement),
211 #[serde(rename = "timepicker")]
212 TimePicker(SlackBlockTimePickerElement),
213 #[serde(rename = "plain_text_input")]
214 PlainTextInput(SlackBlockPlainTextInputElement),
215 #[serde(rename = "number_input")]
216 NumberInput(SlackBlockNumberInputElement),
217 #[serde(rename = "url_text_input")]
218 UrlInput(SlackBlockUrlInputElement),
219 #[serde(rename = "radio_buttons")]
220 RadioButtons(SlackBlockRadioButtonsElement),
221 #[serde(rename = "checkboxes")]
222 Checkboxes(SlackBlockCheckboxesElement),
223 #[serde(rename = "workflow_button")]
224 WorkflowButton(SlackBlockWorkflowButtonElement),
225}
226
227#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
228#[serde(tag = "type")]
229pub enum SlackActionBlockElement {
230 #[serde(rename = "button")]
231 Button(SlackBlockButtonElement),
232 #[serde(rename = "overflow")]
233 Overflow(SlackBlockOverflowElement),
234 #[serde(rename = "datepicker")]
235 DatePicker(SlackBlockDatePickerElement),
236 #[serde(rename = "timepicker")]
237 TimePicker(SlackBlockTimePickerElement),
238 #[serde(rename = "datetimepicker")]
239 DateTimePicker(SlackBlockDateTimePickerElement),
240 #[serde(rename = "plain_text_input")]
241 PlainTextInput(SlackBlockPlainTextInputElement),
242 #[serde(rename = "number_input")]
243 NumberInput(SlackBlockNumberInputElement),
244 #[serde(rename = "url_text_input")]
245 UrlInput(SlackBlockUrlInputElement),
246 #[serde(rename = "radio_buttons")]
247 RadioButtons(SlackBlockRadioButtonsElement),
248 #[serde(rename = "checkboxes")]
249 Checkboxes(SlackBlockCheckboxesElement),
250 #[serde(rename = "static_select")]
251 StaticSelect(SlackBlockStaticSelectElement),
252 #[serde(rename = "external_select")]
253 ExternalSelect(SlackBlockExternalSelectElement),
254 #[serde(rename = "users_select")]
255 UsersSelect(SlackBlockUsersSelectElement),
256 #[serde(rename = "conversations_select")]
257 ConversationsSelect(SlackBlockConversationsSelectElement),
258 #[serde(rename = "channels_select")]
259 ChannelsSelect(SlackBlockChannelsSelectElement),
260 #[serde(rename = "workflow_button")]
261 WorkflowButton(SlackBlockWorkflowButtonElement),
262}
263
264#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
265#[serde(tag = "type")]
266pub enum SlackContextBlockElement {
267 #[serde(rename = "image")]
268 Image(SlackBlockImageElement),
269 #[serde(rename = "plain_text")]
270 Plain(SlackBlockPlainText),
271 #[serde(rename = "mrkdwn")]
272 MarkDown(SlackBlockMarkDownText),
273}
274
275#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
276#[serde(tag = "type")]
277pub enum SlackInputBlockElement {
278 #[serde(rename = "static_select")]
279 StaticSelect(SlackBlockStaticSelectElement),
280 #[serde(rename = "multi_static_select")]
281 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
282 #[serde(rename = "external_select")]
283 ExternalSelect(SlackBlockExternalSelectElement),
284 #[serde(rename = "multi_external_select")]
285 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
286 #[serde(rename = "users_select")]
287 UsersSelect(SlackBlockUsersSelectElement),
288 #[serde(rename = "multi_users_select")]
289 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
290 #[serde(rename = "conversations_select")]
291 ConversationsSelect(SlackBlockConversationsSelectElement),
292 #[serde(rename = "multi_conversations_select")]
293 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
294 #[serde(rename = "channels_select")]
295 ChannelsSelect(SlackBlockChannelsSelectElement),
296 #[serde(rename = "multi_channels_select")]
297 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
298 #[serde(rename = "datepicker")]
299 DatePicker(SlackBlockDatePickerElement),
300 #[serde(rename = "timepicker")]
301 TimePicker(SlackBlockTimePickerElement),
302 #[serde(rename = "datetimepicker")]
303 DateTimePicker(SlackBlockDateTimePickerElement),
304 #[serde(rename = "plain_text_input")]
305 PlainTextInput(SlackBlockPlainTextInputElement),
306 #[serde(rename = "number_input")]
307 NumberInput(SlackBlockNumberInputElement),
308 #[serde(rename = "url_text_input")]
309 UrlInput(SlackBlockUrlInputElement),
310 #[serde(rename = "radio_buttons")]
311 RadioButtons(SlackBlockRadioButtonsElement),
312 #[serde(rename = "checkboxes")]
313 Checkboxes(SlackBlockCheckboxesElement),
314 #[serde(rename = "email_text_input")]
315 EmailInput(SlackBlockEmailInputElement),
316 #[serde(rename = "rich_text_input")]
317 RichTextInput(SlackBlockRichTextInputElement),
318 #[serde(rename = "file_input")]
319 FileInput(SlackBlockFileInputElement),
320}
321
322#[skip_serializing_none]
323#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
324pub struct SlackBlockImageElement {
325 #[serde(flatten)]
326 pub image_url_or_file: SlackImageUrlOrFile,
327 pub alt_text: String,
328}
329
330impl From<SlackBlockImageElement> for SlackSectionBlockElement {
331 fn from(element: SlackBlockImageElement) -> Self {
332 SlackSectionBlockElement::Image(element)
333 }
334}
335
336impl From<SlackBlockImageElement> for SlackContextBlockElement {
337 fn from(element: SlackBlockImageElement) -> Self {
338 SlackContextBlockElement::Image(element)
339 }
340}
341
342#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
343#[serde(rename_all = "snake_case")]
344pub enum SlackBlockButtonStyle {
345 Primary,
346 Danger,
347}
348
349#[skip_serializing_none]
350#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
351pub struct SlackBlockButtonElement {
352 pub action_id: SlackActionId,
353 pub text: SlackBlockPlainTextOnly,
354 pub url: Option<Url>,
355 pub value: Option<String>,
356 pub style: Option<SlackBlockButtonStyle>,
357 pub confirm: Option<SlackBlockConfirmItem>,
358 pub accessibility_label: Option<SlackAccessibilityLabel>,
359}
360
361impl From<SlackBlockButtonElement> for SlackSectionBlockElement {
362 fn from(element: SlackBlockButtonElement) -> Self {
363 SlackSectionBlockElement::Button(element)
364 }
365}
366
367impl From<SlackBlockButtonElement> for SlackActionBlockElement {
368 fn from(element: SlackBlockButtonElement) -> Self {
369 SlackActionBlockElement::Button(element)
370 }
371}
372
373#[skip_serializing_none]
374#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
375pub struct SlackBlockConfirmItem {
376 pub title: SlackBlockPlainTextOnly,
377 pub text: SlackBlockText,
378 pub confirm: SlackBlockPlainTextOnly,
379 pub deny: SlackBlockPlainTextOnly,
380 pub style: Option<String>,
381}
382
383#[skip_serializing_none]
384#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
385pub struct SlackBlockChoiceItem<T: Into<SlackBlockText>> {
386 pub text: T,
387 pub value: String,
388 pub description: Option<SlackBlockPlainTextOnly>,
389 pub url: Option<Url>,
390}
391
392#[skip_serializing_none]
393#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
394pub struct SlackBlockOptionGroup<T: Into<SlackBlockText>> {
395 pub label: SlackBlockPlainTextOnly,
396 pub options: Vec<SlackBlockChoiceItem<T>>,
397}
398
399#[skip_serializing_none]
400#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
401pub struct SlackBlockStaticSelectElement {
402 pub action_id: SlackActionId,
403 pub placeholder: Option<SlackBlockPlainTextOnly>,
404 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
405 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
406 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
407 pub confirm: Option<SlackBlockConfirmItem>,
408 pub focus_on_load: Option<bool>,
409}
410
411impl From<SlackBlockStaticSelectElement> for SlackSectionBlockElement {
412 fn from(element: SlackBlockStaticSelectElement) -> Self {
413 SlackSectionBlockElement::StaticSelect(element)
414 }
415}
416
417impl From<SlackBlockStaticSelectElement> for SlackInputBlockElement {
418 fn from(element: SlackBlockStaticSelectElement) -> Self {
419 SlackInputBlockElement::StaticSelect(element)
420 }
421}
422
423impl From<SlackBlockStaticSelectElement> for SlackActionBlockElement {
424 fn from(element: SlackBlockStaticSelectElement) -> Self {
425 SlackActionBlockElement::StaticSelect(element)
426 }
427}
428
429#[skip_serializing_none]
430#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
431pub struct SlackBlockMultiStaticSelectElement {
432 pub action_id: SlackActionId,
433 pub placeholder: Option<SlackBlockPlainTextOnly>,
434 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
435 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
436 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
437 pub confirm: Option<SlackBlockConfirmItem>,
438 pub max_selected_items: Option<u64>,
439 pub focus_on_load: Option<bool>,
440}
441
442impl From<SlackBlockMultiStaticSelectElement> for SlackSectionBlockElement {
443 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
444 SlackSectionBlockElement::MultiStaticSelect(element)
445 }
446}
447
448impl From<SlackBlockMultiStaticSelectElement> for SlackInputBlockElement {
449 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
450 SlackInputBlockElement::MultiStaticSelect(element)
451 }
452}
453
454#[skip_serializing_none]
455#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
456pub struct SlackBlockExternalSelectElement {
457 pub action_id: SlackActionId,
458 pub placeholder: Option<SlackBlockPlainTextOnly>,
459 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
460 pub confirm: Option<SlackBlockConfirmItem>,
461 pub focus_on_load: Option<bool>,
462 pub min_query_length: Option<u64>,
463}
464
465impl From<SlackBlockExternalSelectElement> for SlackSectionBlockElement {
466 fn from(element: SlackBlockExternalSelectElement) -> Self {
467 SlackSectionBlockElement::ExternalSelect(element)
468 }
469}
470
471impl From<SlackBlockExternalSelectElement> for SlackInputBlockElement {
472 fn from(element: SlackBlockExternalSelectElement) -> Self {
473 SlackInputBlockElement::ExternalSelect(element)
474 }
475}
476
477impl From<SlackBlockExternalSelectElement> for SlackActionBlockElement {
478 fn from(element: SlackBlockExternalSelectElement) -> Self {
479 SlackActionBlockElement::ExternalSelect(element)
480 }
481}
482
483#[skip_serializing_none]
484#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
485pub struct SlackBlockMultiExternalSelectElement {
486 pub action_id: SlackActionId,
487 pub placeholder: Option<SlackBlockPlainTextOnly>,
488 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
489 pub confirm: Option<SlackBlockConfirmItem>,
490 pub max_selected_items: Option<u64>,
491 pub focus_on_load: Option<bool>,
492 pub min_query_length: Option<u64>,
493}
494
495impl From<SlackBlockMultiExternalSelectElement> for SlackSectionBlockElement {
496 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
497 SlackSectionBlockElement::MultiExternalSelect(element)
498 }
499}
500
501impl From<SlackBlockMultiExternalSelectElement> for SlackInputBlockElement {
502 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
503 SlackInputBlockElement::MultiExternalSelect(element)
504 }
505}
506
507#[skip_serializing_none]
508#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
509pub struct SlackBlockUsersSelectElement {
510 pub action_id: SlackActionId,
511 pub placeholder: Option<SlackBlockPlainTextOnly>,
512 pub initial_user: Option<String>,
513 pub confirm: Option<SlackBlockConfirmItem>,
514 pub focus_on_load: Option<bool>,
515}
516
517impl From<SlackBlockUsersSelectElement> for SlackSectionBlockElement {
518 fn from(element: SlackBlockUsersSelectElement) -> Self {
519 SlackSectionBlockElement::UsersSelect(element)
520 }
521}
522
523impl From<SlackBlockUsersSelectElement> for SlackInputBlockElement {
524 fn from(element: SlackBlockUsersSelectElement) -> Self {
525 SlackInputBlockElement::UsersSelect(element)
526 }
527}
528
529impl From<SlackBlockUsersSelectElement> for SlackActionBlockElement {
530 fn from(element: SlackBlockUsersSelectElement) -> Self {
531 SlackActionBlockElement::UsersSelect(element)
532 }
533}
534
535#[skip_serializing_none]
536#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
537pub struct SlackBlockMultiUsersSelectElement {
538 pub action_id: SlackActionId,
539 pub placeholder: Option<SlackBlockPlainTextOnly>,
540 pub initial_users: Option<Vec<String>>,
541 pub confirm: Option<SlackBlockConfirmItem>,
542 pub max_selected_items: Option<u64>,
543 pub focus_on_load: Option<bool>,
544}
545
546impl From<SlackBlockMultiUsersSelectElement> for SlackSectionBlockElement {
547 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
548 SlackSectionBlockElement::MultiUsersSelect(element)
549 }
550}
551
552impl From<SlackBlockMultiUsersSelectElement> for SlackInputBlockElement {
553 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
554 SlackInputBlockElement::MultiUsersSelect(element)
555 }
556}
557
558#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
559pub enum SlackConversationFilterInclude {
560 #[serde(rename = "im")]
561 Im,
562 #[serde(rename = "mpim")]
563 Mpim,
564 #[serde(rename = "public")]
565 Public,
566 #[serde(rename = "private")]
567 Private,
568}
569
570#[skip_serializing_none]
571#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
572pub struct SlackBlockConversationFilter {
573 pub include: Option<Vec<SlackConversationFilterInclude>>,
574 pub exclude_external_shared_channels: Option<bool>,
575 pub exclude_bot_users: Option<bool>,
576}
577
578#[skip_serializing_none]
579#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
580pub struct SlackBlockConversationsSelectElement {
581 pub action_id: SlackActionId,
582 pub placeholder: Option<SlackBlockPlainTextOnly>,
583 pub initial_conversation: Option<SlackConversationId>,
584 pub default_to_current_conversation: Option<bool>,
585 pub confirm: Option<SlackBlockConfirmItem>,
586 pub response_url_enabled: Option<bool>,
587 pub focus_on_load: Option<bool>,
588 pub filter: Option<SlackBlockConversationFilter>,
589}
590
591impl From<SlackBlockConversationsSelectElement> for SlackSectionBlockElement {
592 fn from(element: SlackBlockConversationsSelectElement) -> Self {
593 SlackSectionBlockElement::ConversationsSelect(element)
594 }
595}
596
597impl From<SlackBlockConversationsSelectElement> for SlackInputBlockElement {
598 fn from(element: SlackBlockConversationsSelectElement) -> Self {
599 SlackInputBlockElement::ConversationsSelect(element)
600 }
601}
602
603impl From<SlackBlockConversationsSelectElement> for SlackActionBlockElement {
604 fn from(element: SlackBlockConversationsSelectElement) -> Self {
605 SlackActionBlockElement::ConversationsSelect(element)
606 }
607}
608
609#[skip_serializing_none]
610#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
611pub struct SlackBlockMultiConversationsSelectElement {
612 pub action_id: SlackActionId,
613 pub placeholder: Option<SlackBlockPlainTextOnly>,
614 pub initial_conversations: Option<Vec<SlackConversationId>>,
615 pub default_to_current_conversation: Option<bool>,
616 pub confirm: Option<SlackBlockConfirmItem>,
617 pub max_selected_items: Option<u64>,
618 pub focus_on_load: Option<bool>,
619 pub filter: Option<SlackBlockConversationFilter>,
620}
621
622impl From<SlackBlockMultiConversationsSelectElement> for SlackSectionBlockElement {
623 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
624 SlackSectionBlockElement::MultiConversationsSelect(element)
625 }
626}
627
628impl From<SlackBlockMultiConversationsSelectElement> for SlackInputBlockElement {
629 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
630 SlackInputBlockElement::MultiConversationsSelect(element)
631 }
632}
633
634#[skip_serializing_none]
635#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
636pub struct SlackBlockChannelsSelectElement {
637 pub action_id: SlackActionId,
638 pub placeholder: Option<SlackBlockPlainTextOnly>,
639 pub initial_channel: Option<SlackChannelId>,
640 pub confirm: Option<SlackBlockConfirmItem>,
641 pub response_url_enabled: Option<bool>,
642 pub focus_on_load: Option<bool>,
643}
644
645impl From<SlackBlockChannelsSelectElement> for SlackSectionBlockElement {
646 fn from(element: SlackBlockChannelsSelectElement) -> Self {
647 SlackSectionBlockElement::ChannelsSelect(element)
648 }
649}
650
651impl From<SlackBlockChannelsSelectElement> for SlackInputBlockElement {
652 fn from(element: SlackBlockChannelsSelectElement) -> Self {
653 SlackInputBlockElement::ChannelsSelect(element)
654 }
655}
656
657impl From<SlackBlockChannelsSelectElement> for SlackActionBlockElement {
658 fn from(element: SlackBlockChannelsSelectElement) -> Self {
659 SlackActionBlockElement::ChannelsSelect(element)
660 }
661}
662
663#[skip_serializing_none]
664#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
665pub struct SlackBlockMultiChannelsSelectElement {
666 pub action_id: SlackActionId,
667 pub placeholder: Option<SlackBlockPlainTextOnly>,
668 pub initial_channels: Option<Vec<SlackChannelId>>,
669 pub confirm: Option<SlackBlockConfirmItem>,
670 pub max_selected_items: Option<u64>,
671 pub focus_on_load: Option<bool>,
672}
673
674impl From<SlackBlockMultiChannelsSelectElement> for SlackSectionBlockElement {
675 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
676 SlackSectionBlockElement::MultiChannelsSelect(element)
677 }
678}
679
680impl From<SlackBlockMultiChannelsSelectElement> for SlackInputBlockElement {
681 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
682 SlackInputBlockElement::MultiChannelsSelect(element)
683 }
684}
685
686#[skip_serializing_none]
687#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
688pub struct SlackBlockOverflowElement {
689 pub action_id: SlackActionId,
690 pub options: Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
691 pub confirm: Option<SlackBlockConfirmItem>,
692}
693
694impl From<SlackBlockOverflowElement> for SlackSectionBlockElement {
695 fn from(element: SlackBlockOverflowElement) -> Self {
696 SlackSectionBlockElement::Overflow(element)
697 }
698}
699
700impl From<SlackBlockOverflowElement> for SlackActionBlockElement {
701 fn from(element: SlackBlockOverflowElement) -> Self {
702 SlackActionBlockElement::Overflow(element)
703 }
704}
705
706#[skip_serializing_none]
707#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
708pub struct SlackBlockDatePickerElement {
709 pub action_id: SlackActionId,
710 pub placeholder: Option<SlackBlockPlainTextOnly>,
711 pub initial_date: Option<String>,
712 pub confirm: Option<SlackBlockConfirmItem>,
713 pub focus_on_load: Option<bool>,
714}
715
716impl From<SlackBlockDatePickerElement> for SlackSectionBlockElement {
717 fn from(element: SlackBlockDatePickerElement) -> Self {
718 SlackSectionBlockElement::DatePicker(element)
719 }
720}
721
722impl From<SlackBlockDatePickerElement> for SlackInputBlockElement {
723 fn from(element: SlackBlockDatePickerElement) -> Self {
724 SlackInputBlockElement::DatePicker(element)
725 }
726}
727
728impl From<SlackBlockDatePickerElement> for SlackActionBlockElement {
729 fn from(element: SlackBlockDatePickerElement) -> Self {
730 SlackActionBlockElement::DatePicker(element)
731 }
732}
733
734#[skip_serializing_none]
735#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
736pub struct SlackBlockTimePickerElement {
737 pub action_id: SlackActionId,
738 pub initial_time: Option<String>,
739 pub confirm: Option<SlackBlockConfirmItem>,
740 pub focus_on_load: Option<bool>,
741 pub placeholder: Option<SlackBlockPlainTextOnly>,
742 pub timezone: Option<String>,
743}
744
745impl From<SlackBlockTimePickerElement> for SlackSectionBlockElement {
746 fn from(element: SlackBlockTimePickerElement) -> Self {
747 SlackSectionBlockElement::TimePicker(element)
748 }
749}
750
751impl From<SlackBlockTimePickerElement> for SlackInputBlockElement {
752 fn from(element: SlackBlockTimePickerElement) -> Self {
753 SlackInputBlockElement::TimePicker(element)
754 }
755}
756
757impl From<SlackBlockTimePickerElement> for SlackActionBlockElement {
758 fn from(element: SlackBlockTimePickerElement) -> Self {
759 SlackActionBlockElement::TimePicker(element)
760 }
761}
762
763#[skip_serializing_none]
764#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
765pub struct SlackBlockDateTimePickerElement {
766 pub action_id: SlackActionId,
767 pub initial_date_time: Option<SlackDateTime>,
768 pub confirm: Option<SlackBlockConfirmItem>,
769 pub focus_on_load: Option<bool>,
770}
771
772impl From<SlackBlockDateTimePickerElement> for SlackInputBlockElement {
773 fn from(element: SlackBlockDateTimePickerElement) -> Self {
774 SlackInputBlockElement::DateTimePicker(element)
775 }
776}
777
778impl From<SlackBlockDateTimePickerElement> for SlackActionBlockElement {
779 fn from(element: SlackBlockDateTimePickerElement) -> Self {
780 SlackActionBlockElement::DateTimePicker(element)
781 }
782}
783
784#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
788#[serde(rename_all = "snake_case")]
789pub enum SlackDispatchActionTrigger {
790 OnEnterPressed,
791 OnCharacterEntered,
792}
793
794#[skip_serializing_none]
795#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
796pub struct SlackDispatchActionConfig {
797 pub trigger_actions_on: Option<Vec<SlackDispatchActionTrigger>>,
798}
799
800#[skip_serializing_none]
801#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
802pub struct SlackBlockPlainTextInputElement {
803 pub action_id: SlackActionId,
804 pub placeholder: Option<SlackBlockPlainTextOnly>,
805 pub initial_value: Option<String>,
806 pub multiline: Option<bool>,
807 pub min_length: Option<u64>,
808 pub max_length: Option<u64>,
809 pub focus_on_load: Option<bool>,
810 pub dispatch_action_config: Option<SlackDispatchActionConfig>,
811}
812
813impl From<SlackBlockPlainTextInputElement> for SlackSectionBlockElement {
814 fn from(element: SlackBlockPlainTextInputElement) -> Self {
815 SlackSectionBlockElement::PlainTextInput(element)
816 }
817}
818
819impl From<SlackBlockPlainTextInputElement> for SlackInputBlockElement {
820 fn from(element: SlackBlockPlainTextInputElement) -> Self {
821 SlackInputBlockElement::PlainTextInput(element)
822 }
823}
824
825impl From<SlackBlockPlainTextInputElement> for SlackActionBlockElement {
826 fn from(element: SlackBlockPlainTextInputElement) -> Self {
827 SlackActionBlockElement::PlainTextInput(element)
828 }
829}
830
831#[skip_serializing_none]
832#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
833pub struct SlackBlockNumberInputElement {
834 pub action_id: SlackActionId,
835 pub is_decimal_allowed: bool,
836 pub focus_on_load: Option<bool>,
837 pub placeholder: Option<SlackBlockPlainTextOnly>,
838 pub initial_value: Option<String>,
839 pub min_value: Option<String>,
840 pub max_value: Option<String>,
841}
842
843impl From<SlackBlockNumberInputElement> for SlackSectionBlockElement {
844 fn from(element: SlackBlockNumberInputElement) -> Self {
845 SlackSectionBlockElement::NumberInput(element)
846 }
847}
848
849impl From<SlackBlockNumberInputElement> for SlackInputBlockElement {
850 fn from(element: SlackBlockNumberInputElement) -> Self {
851 SlackInputBlockElement::NumberInput(element)
852 }
853}
854
855impl From<SlackBlockNumberInputElement> for SlackActionBlockElement {
856 fn from(element: SlackBlockNumberInputElement) -> Self {
857 SlackActionBlockElement::NumberInput(element)
858 }
859}
860
861#[skip_serializing_none]
862#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
863pub struct SlackBlockUrlInputElement {
864 pub action_id: SlackActionId,
865 pub placeholder: Option<SlackBlockPlainTextOnly>,
866 pub initial_value: Option<String>,
867}
868
869impl From<SlackBlockUrlInputElement> for SlackSectionBlockElement {
870 fn from(element: SlackBlockUrlInputElement) -> Self {
871 SlackSectionBlockElement::UrlInput(element)
872 }
873}
874
875impl From<SlackBlockUrlInputElement> for SlackInputBlockElement {
876 fn from(element: SlackBlockUrlInputElement) -> Self {
877 SlackInputBlockElement::UrlInput(element)
878 }
879}
880
881impl From<SlackBlockUrlInputElement> for SlackActionBlockElement {
882 fn from(element: SlackBlockUrlInputElement) -> Self {
883 SlackActionBlockElement::UrlInput(element)
884 }
885}
886
887#[skip_serializing_none]
888#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
889pub struct SlackBlockEmailInputElement {
890 pub action_id: SlackActionId,
891 pub focus_on_load: Option<bool>,
892 pub placeholder: Option<SlackBlockPlainTextOnly>,
893 pub initial_value: Option<EmailAddress>,
894}
895
896impl From<SlackBlockEmailInputElement> for SlackInputBlockElement {
897 fn from(element: SlackBlockEmailInputElement) -> Self {
898 SlackInputBlockElement::EmailInput(element)
899 }
900}
901
902#[skip_serializing_none]
903#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
904pub struct SlackBlockRadioButtonsElement {
905 pub action_id: SlackActionId,
906 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
907 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockText>>,
908 pub confirm: Option<SlackBlockConfirmItem>,
909 pub focus_on_load: Option<bool>,
910}
911
912impl From<SlackBlockRadioButtonsElement> for SlackSectionBlockElement {
913 fn from(element: SlackBlockRadioButtonsElement) -> Self {
914 SlackSectionBlockElement::RadioButtons(element)
915 }
916}
917
918impl From<SlackBlockRadioButtonsElement> for SlackInputBlockElement {
919 fn from(element: SlackBlockRadioButtonsElement) -> Self {
920 SlackInputBlockElement::RadioButtons(element)
921 }
922}
923
924impl From<SlackBlockRadioButtonsElement> for SlackActionBlockElement {
925 fn from(element: SlackBlockRadioButtonsElement) -> Self {
926 SlackActionBlockElement::RadioButtons(element)
927 }
928}
929
930#[skip_serializing_none]
931#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
932pub struct SlackBlockCheckboxesElement {
933 pub action_id: SlackActionId,
934 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
935 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockText>>>,
936 pub confirm: Option<SlackBlockConfirmItem>,
937 pub focus_on_load: Option<bool>,
938}
939
940impl From<SlackBlockCheckboxesElement> for SlackSectionBlockElement {
941 fn from(element: SlackBlockCheckboxesElement) -> Self {
942 SlackSectionBlockElement::Checkboxes(element)
943 }
944}
945
946impl From<SlackBlockCheckboxesElement> for SlackInputBlockElement {
947 fn from(element: SlackBlockCheckboxesElement) -> Self {
948 SlackInputBlockElement::Checkboxes(element)
949 }
950}
951
952impl From<SlackBlockCheckboxesElement> for SlackActionBlockElement {
953 fn from(element: SlackBlockCheckboxesElement) -> Self {
954 SlackActionBlockElement::Checkboxes(element)
955 }
956}
957
958#[skip_serializing_none]
962#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
963pub struct SlackBlockPlainText {
964 pub text: String,
965 pub emoji: Option<bool>,
966}
967
968#[skip_serializing_none]
972#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
973pub struct SlackBlockMarkDownText {
974 pub text: String,
975 pub verbatim: Option<bool>,
976}
977
978#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
982#[serde(tag = "type")]
983pub enum SlackBlockText {
984 #[serde(rename = "plain_text")]
985 Plain(SlackBlockPlainText),
986 #[serde(rename = "mrkdwn")]
987 MarkDown(SlackBlockMarkDownText),
988}
989
990#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
991#[serde(tag = "type", rename = "plain_text")]
992pub struct SlackBlockPlainTextOnly {
993 #[serde(flatten)]
994 value: SlackBlockPlainText,
995}
996
997impl SlackBlockPlainText {
998 pub fn as_block_text(&self) -> SlackBlockText {
999 SlackBlockText::Plain(self.clone())
1000 }
1001}
1002
1003impl From<String> for SlackBlockPlainText {
1004 fn from(value: String) -> Self {
1005 SlackBlockPlainText::new(value)
1006 }
1007}
1008
1009impl From<&str> for SlackBlockPlainText {
1010 fn from(value: &str) -> Self {
1011 SlackBlockPlainText::new(String::from(value))
1012 }
1013}
1014
1015impl SlackBlockMarkDownText {
1016 pub fn as_block_text(&self) -> SlackBlockText {
1017 SlackBlockText::MarkDown(self.clone())
1018 }
1019}
1020
1021impl From<String> for SlackBlockMarkDownText {
1022 fn from(value: String) -> Self {
1023 SlackBlockMarkDownText::new(value)
1024 }
1025}
1026
1027impl From<&str> for SlackBlockMarkDownText {
1028 fn from(value: &str) -> Self {
1029 SlackBlockMarkDownText::new(String::from(value))
1030 }
1031}
1032
1033impl From<SlackBlockPlainText> for SlackBlockPlainTextOnly {
1034 fn from(pt: SlackBlockPlainText) -> Self {
1035 SlackBlockPlainTextOnly { value: pt }
1036 }
1037}
1038
1039impl From<SlackBlockPlainText> for SlackBlockText {
1040 fn from(text: SlackBlockPlainText) -> Self {
1041 SlackBlockText::Plain(text)
1042 }
1043}
1044
1045impl From<SlackBlockMarkDownText> for SlackBlockText {
1046 fn from(text: SlackBlockMarkDownText) -> Self {
1047 SlackBlockText::MarkDown(text)
1048 }
1049}
1050
1051impl From<SlackBlockPlainText> for SlackContextBlockElement {
1052 fn from(text: SlackBlockPlainText) -> Self {
1053 SlackContextBlockElement::Plain(text)
1054 }
1055}
1056
1057impl From<SlackBlockMarkDownText> for SlackContextBlockElement {
1058 fn from(text: SlackBlockMarkDownText) -> Self {
1059 SlackContextBlockElement::MarkDown(text)
1060 }
1061}
1062
1063impl From<SlackBlockPlainTextOnly> for SlackBlockText {
1064 fn from(text: SlackBlockPlainTextOnly) -> Self {
1065 SlackBlockText::Plain(text.value)
1066 }
1067}
1068
1069impl From<String> for SlackBlockPlainTextOnly {
1070 fn from(value: String) -> Self {
1071 SlackBlockPlainTextOnly {
1072 value: value.into(),
1073 }
1074 }
1075}
1076
1077impl From<&str> for SlackBlockPlainTextOnly {
1078 fn from(value: &str) -> Self {
1079 SlackBlockPlainTextOnly {
1080 value: value.into(),
1081 }
1082 }
1083}
1084
1085#[skip_serializing_none]
1089#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1090pub struct SlackVideoBlock {
1091 pub alt_text: String,
1092 pub author_name: Option<String>,
1093 pub block_id: Option<SlackBlockId>,
1094 pub description: Option<SlackBlockPlainTextOnly>,
1095 pub provider_icon_url: Option<Url>,
1096 pub provider_name: Option<String>,
1097 pub title: SlackBlockPlainTextOnly,
1098 pub title_url: Option<Url>,
1099 pub thumbnail_url: Url,
1100 pub video_url: Url,
1101}
1102
1103impl From<SlackVideoBlock> for SlackBlock {
1104 fn from(block: SlackVideoBlock) -> Self {
1105 SlackBlock::Video(block)
1106 }
1107}
1108
1109#[skip_serializing_none]
1113#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1114pub struct SlackMarkdownBlock {
1115 pub block_id: Option<SlackBlockId>,
1116 pub text: String,
1117}
1118
1119impl From<SlackMarkdownBlock> for SlackBlock {
1120 fn from(block: SlackMarkdownBlock) -> Self {
1121 SlackBlock::Markdown(block)
1122 }
1123}
1124
1125#[skip_serializing_none]
1129#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1130pub struct SlackRichTextBlock {
1131 pub block_id: Option<SlackBlockId>,
1132 pub elements: Vec<SlackRichTextElement>,
1133}
1134
1135impl From<SlackRichTextBlock> for SlackBlock {
1136 fn from(block: SlackRichTextBlock) -> Self {
1137 SlackBlock::RichText(block)
1138 }
1139}
1140
1141#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1142#[serde(tag = "type", rename_all = "snake_case")]
1143pub enum SlackRichTextInlineContent {
1144 #[serde(rename = "rich_text")]
1145 RichText(SlackRichTextBlock),
1146}
1147
1148impl From<SlackRichTextBlock> for SlackRichTextInlineContent {
1149 fn from(block: SlackRichTextBlock) -> Self {
1150 SlackRichTextInlineContent::RichText(block)
1151 }
1152}
1153
1154#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1155#[serde(tag = "type")]
1156pub enum SlackRichTextElement {
1157 #[serde(rename = "rich_text_section")]
1158 Section(SlackRichTextSection),
1159 #[serde(rename = "rich_text_list")]
1160 List(SlackRichTextList),
1161 #[serde(rename = "rich_text_preformatted")]
1162 Preformatted(SlackRichTextPreformatted),
1163 #[serde(rename = "rich_text_quote")]
1164 Quote(SlackRichTextQuote),
1165}
1166
1167impl From<SlackRichTextSection> for SlackRichTextElement {
1168 fn from(element: SlackRichTextSection) -> Self {
1169 SlackRichTextElement::Section(element)
1170 }
1171}
1172
1173impl From<SlackRichTextList> for SlackRichTextElement {
1174 fn from(list: SlackRichTextList) -> Self {
1175 SlackRichTextElement::List(list)
1176 }
1177}
1178
1179impl From<SlackRichTextPreformatted> for SlackRichTextElement {
1180 fn from(element: SlackRichTextPreformatted) -> Self {
1181 SlackRichTextElement::Preformatted(element)
1182 }
1183}
1184
1185impl From<SlackRichTextQuote> for SlackRichTextElement {
1186 fn from(element: SlackRichTextQuote) -> Self {
1187 SlackRichTextElement::Quote(element)
1188 }
1189}
1190
1191#[skip_serializing_none]
1192#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1193pub struct SlackRichTextSection {
1194 pub elements: Vec<SlackRichTextInlineElement>,
1195}
1196
1197impl From<&str> for SlackRichTextSection {
1199 fn from(value: &str) -> Self {
1200 SlackRichTextSection::new(vec![value.into()])
1201 }
1202}
1203
1204impl From<String> for SlackRichTextSection {
1205 fn from(value: String) -> Self {
1206 SlackRichTextSection::new(vec![value.into()])
1207 }
1208}
1209
1210#[skip_serializing_none]
1211#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1212pub struct SlackRichTextList {
1213 pub style: SlackRichTextListStyle,
1214 pub elements: Vec<SlackRichTextListElement>,
1215 pub indent: Option<u64>,
1216 pub offset: Option<u64>,
1217 pub border: Option<u64>,
1218}
1219
1220#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1221#[serde(tag = "type")]
1222pub enum SlackRichTextListElement {
1223 #[serde(rename = "rich_text_section")]
1224 Section(SlackRichTextSection),
1225}
1226
1227impl From<SlackRichTextSection> for SlackRichTextListElement {
1228 fn from(element: SlackRichTextSection) -> Self {
1229 SlackRichTextListElement::Section(element)
1230 }
1231}
1232
1233impl From<&str> for SlackRichTextListElement {
1235 fn from(value: &str) -> Self {
1236 SlackRichTextListElement::Section(value.into())
1237 }
1238}
1239
1240impl From<String> for SlackRichTextListElement {
1241 fn from(value: String) -> Self {
1242 SlackRichTextListElement::Section(value.into())
1243 }
1244}
1245
1246#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1247#[serde(rename_all = "snake_case")]
1248pub enum SlackRichTextListStyle {
1249 Bullet,
1250 Ordered,
1251}
1252
1253#[skip_serializing_none]
1254#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1255pub struct SlackRichTextPreformatted {
1256 pub elements: Vec<SlackRichTextInlineElement>,
1257 pub border: Option<u64>,
1258 pub language: Option<String>,
1259}
1260
1261#[skip_serializing_none]
1262#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1263pub struct SlackRichTextQuote {
1264 pub elements: Vec<SlackRichTextInlineElement>,
1265 pub border: Option<u64>,
1266}
1267
1268#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1269#[serde(tag = "type")]
1270pub enum SlackRichTextInlineElement {
1271 #[serde(rename = "text")]
1272 Text(SlackRichTextText),
1273 #[serde(rename = "link")]
1274 Link(SlackRichTextLink),
1275 #[serde(rename = "user")]
1276 User(SlackRichTextUser),
1277 #[serde(rename = "channel")]
1278 Channel(SlackRichTextChannel),
1279 #[serde(rename = "usergroup")]
1280 UserGroup(SlackRichTextUserGroup),
1281 #[serde(rename = "emoji")]
1282 Emoji(SlackRichTextEmoji),
1283 #[serde(rename = "date")]
1284 Date(SlackRichTextDate),
1285 #[serde(rename = "broadcast")]
1286 Broadcast(SlackRichTextBroadcast),
1287 #[serde(rename = "color")]
1288 Color(SlackRichTextColor),
1289 #[serde(rename = "message_mention")]
1290 MessageMention(SlackRichTextMessageMention),
1291 #[serde(untagged)]
1292 Unknown(serde_json::Value),
1293}
1294
1295impl From<&str> for SlackRichTextInlineElement {
1297 fn from(value: &str) -> Self {
1298 SlackRichTextInlineElement::Text(SlackRichTextText::new(value.to_string()))
1299 }
1300}
1301
1302impl From<String> for SlackRichTextInlineElement {
1303 fn from(value: String) -> Self {
1304 SlackRichTextInlineElement::Text(SlackRichTextText::new(value))
1305 }
1306}
1307
1308impl From<SlackRichTextText> for SlackRichTextInlineElement {
1309 fn from(element: SlackRichTextText) -> Self {
1310 SlackRichTextInlineElement::Text(element)
1311 }
1312}
1313
1314impl From<SlackRichTextLink> for SlackRichTextInlineElement {
1315 fn from(element: SlackRichTextLink) -> Self {
1316 SlackRichTextInlineElement::Link(element)
1317 }
1318}
1319
1320impl From<SlackRichTextUser> for SlackRichTextInlineElement {
1321 fn from(element: SlackRichTextUser) -> Self {
1322 SlackRichTextInlineElement::User(element)
1323 }
1324}
1325
1326impl From<SlackRichTextChannel> for SlackRichTextInlineElement {
1327 fn from(element: SlackRichTextChannel) -> Self {
1328 SlackRichTextInlineElement::Channel(element)
1329 }
1330}
1331
1332impl From<SlackRichTextUserGroup> for SlackRichTextInlineElement {
1333 fn from(element: SlackRichTextUserGroup) -> Self {
1334 SlackRichTextInlineElement::UserGroup(element)
1335 }
1336}
1337
1338impl From<SlackRichTextEmoji> for SlackRichTextInlineElement {
1339 fn from(element: SlackRichTextEmoji) -> Self {
1340 SlackRichTextInlineElement::Emoji(element)
1341 }
1342}
1343
1344impl From<SlackRichTextDate> for SlackRichTextInlineElement {
1345 fn from(element: SlackRichTextDate) -> Self {
1346 SlackRichTextInlineElement::Date(element)
1347 }
1348}
1349
1350impl From<SlackRichTextBroadcast> for SlackRichTextInlineElement {
1351 fn from(element: SlackRichTextBroadcast) -> Self {
1352 SlackRichTextInlineElement::Broadcast(element)
1353 }
1354}
1355
1356impl From<SlackRichTextColor> for SlackRichTextInlineElement {
1357 fn from(element: SlackRichTextColor) -> Self {
1358 SlackRichTextInlineElement::Color(element)
1359 }
1360}
1361
1362impl From<SlackRichTextMessageMention> for SlackRichTextInlineElement {
1363 fn from(element: SlackRichTextMessageMention) -> Self {
1364 SlackRichTextInlineElement::MessageMention(element)
1365 }
1366}
1367
1368#[skip_serializing_none]
1369#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1370pub struct SlackRichTextStyle {
1371 pub bold: Option<bool>,
1372 pub italic: Option<bool>,
1373 pub strike: Option<bool>,
1374 pub code: Option<bool>,
1375 pub underline: Option<bool>,
1376 pub highlight: Option<bool>,
1377 pub client_highlight: Option<bool>,
1378 pub unlink: Option<bool>,
1379}
1380
1381#[skip_serializing_none]
1382#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1383pub struct SlackRichTextText {
1384 pub text: String,
1385 pub style: Option<SlackRichTextStyle>,
1386}
1387
1388impl SlackRichTextText {
1389 pub fn bold(mut self) -> Self {
1391 self.style.get_or_insert_with(SlackRichTextStyle::new).bold = Some(true);
1392 self
1393 }
1394
1395 pub fn italic(mut self) -> Self {
1397 self.style
1398 .get_or_insert_with(SlackRichTextStyle::new)
1399 .italic = Some(true);
1400 self
1401 }
1402
1403 pub fn strike(mut self) -> Self {
1405 self.style
1406 .get_or_insert_with(SlackRichTextStyle::new)
1407 .strike = Some(true);
1408 self
1409 }
1410
1411 pub fn code(mut self) -> Self {
1413 self.style.get_or_insert_with(SlackRichTextStyle::new).code = Some(true);
1414 self
1415 }
1416}
1417
1418#[skip_serializing_none]
1419#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1420pub struct SlackRichTextLink {
1421 pub url: SlackRelaxedUrl,
1422 pub text: Option<String>,
1423 #[serde(rename = "unsafe")]
1424 pub unsafe_: Option<bool>,
1425 pub style: Option<SlackRichTextStyle>,
1426}
1427
1428#[skip_serializing_none]
1429#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1430pub struct SlackRichTextUser {
1431 pub user_id: SlackUserId,
1432 pub style: Option<SlackRichTextStyle>,
1433}
1434
1435#[skip_serializing_none]
1436#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1437pub struct SlackRichTextChannel {
1438 pub channel_id: SlackChannelId,
1439 pub style: Option<SlackRichTextStyle>,
1440}
1441
1442#[skip_serializing_none]
1443#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1444pub struct SlackRichTextUserGroup {
1445 pub usergroup_id: SlackUserGroupId,
1446 pub style: Option<SlackRichTextStyle>,
1447}
1448
1449#[skip_serializing_none]
1450#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1451pub struct SlackRichTextEmoji {
1452 pub name: SlackEmojiName,
1453 pub unicode: Option<String>,
1454}
1455
1456#[skip_serializing_none]
1457#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1458pub struct SlackRichTextDate {
1459 pub timestamp: SlackDateTime,
1460 pub format: String,
1461 pub fallback: Option<String>,
1462 pub style: Option<SlackRichTextStyle>,
1463}
1464
1465#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1466#[serde(rename_all = "snake_case")]
1467pub enum SlackRichTextBroadcastRange {
1468 Here,
1469 Channel,
1470 Everyone,
1471}
1472
1473#[skip_serializing_none]
1474#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1475pub struct SlackRichTextBroadcast {
1476 pub range: SlackRichTextBroadcastRange,
1477 pub style: Option<SlackRichTextStyle>,
1478}
1479
1480#[skip_serializing_none]
1481#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1482pub struct SlackRichTextColor {
1483 pub value: String,
1484}
1485
1486#[skip_serializing_none]
1487#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1488pub struct SlackRichTextMessageMention {
1489 pub url: SlackRelaxedUrl,
1490 pub text: Option<String>,
1491 pub channel_id: Option<SlackChannelId>,
1492 pub author_id: Option<SlackUserId>,
1493 pub message_ts: Option<SlackTs>,
1494 pub thread_ts: Option<SlackTs>,
1495 pub style: Option<SlackRichTextStyle>,
1496}
1497
1498#[skip_serializing_none]
1502#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1503pub struct SlackBlockRichTextInputElement {
1504 pub action_id: SlackActionId,
1505 pub initial_value: Option<SlackRichTextBlock>,
1506 pub focus_on_load: Option<bool>,
1507 pub placeholder: Option<SlackBlockPlainTextOnly>,
1508}
1509
1510impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1511 fn from(element: SlackBlockRichTextInputElement) -> Self {
1512 SlackInputBlockElement::RichTextInput(element)
1513 }
1514}
1515
1516#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1517#[serde(untagged)]
1518pub enum SlackImageUrlOrFile {
1519 ImageUrl { image_url: Url },
1520 SlackFile { slack_file: SlackFileIdOrUrl },
1521}
1522
1523impl SlackImageUrlOrFile {
1524 pub fn image_url(&self) -> Option<&Url> {
1525 match self {
1526 SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1527 SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1528 SlackFileIdOrUrl::Url { url } => Some(url),
1529 _ => None,
1530 },
1531 }
1532 }
1533}
1534
1535impl From<Url> for SlackImageUrlOrFile {
1536 fn from(value: Url) -> Self {
1537 SlackImageUrlOrFile::ImageUrl { image_url: value }
1538 }
1539}
1540
1541#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1542#[serde(untagged)]
1543pub enum SlackFileIdOrUrl {
1544 Id { id: SlackFileId },
1545 Url { url: Url },
1546}
1547
1548impl From<SlackFileId> for SlackFileIdOrUrl {
1549 fn from(value: SlackFileId) -> Self {
1550 SlackFileIdOrUrl::Id { id: value }
1551 }
1552}
1553
1554#[skip_serializing_none]
1558#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1559pub struct SlackTableBlock {
1560 pub block_id: Option<SlackBlockId>,
1561 pub rows: Vec<Vec<SlackTableCell>>,
1562 pub column_settings: Option<Vec<SlackTableColumnSetting>>,
1563}
1564
1565impl From<SlackTableBlock> for SlackBlock {
1566 fn from(block: SlackTableBlock) -> Self {
1567 SlackBlock::Table(block)
1568 }
1569}
1570
1571#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1572#[serde(tag = "type")]
1573pub enum SlackTableCell {
1574 #[serde(rename = "raw_text")]
1575 RawText(SlackTableRawTextCell),
1576 #[serde(rename = "rich_text")]
1577 RichText(SlackTableRichTextCell),
1578}
1579
1580impl From<&str> for SlackTableCell {
1582 fn from(value: &str) -> Self {
1583 SlackTableCell::RawText(SlackTableRawTextCell::new(value.to_string()))
1584 }
1585}
1586
1587impl From<String> for SlackTableCell {
1588 fn from(value: String) -> Self {
1589 SlackTableCell::RawText(SlackTableRawTextCell::new(value))
1590 }
1591}
1592
1593impl From<SlackTableRawTextCell> for SlackTableCell {
1594 fn from(cell: SlackTableRawTextCell) -> Self {
1595 SlackTableCell::RawText(cell)
1596 }
1597}
1598
1599impl From<SlackTableRichTextCell> for SlackTableCell {
1600 fn from(cell: SlackTableRichTextCell) -> Self {
1601 SlackTableCell::RichText(cell)
1602 }
1603}
1604
1605#[skip_serializing_none]
1606#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1607pub struct SlackTableRawTextCell {
1608 pub text: String,
1609}
1610
1611#[skip_serializing_none]
1612#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1613pub struct SlackTableRichTextCell {
1614 pub elements: Vec<SlackRichTextElement>,
1615}
1616
1617#[skip_serializing_none]
1618#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1619pub struct SlackTableColumnSetting {
1620 pub align: Option<SlackTableColumnAlign>,
1621 pub is_wrapped: Option<bool>,
1622}
1623
1624#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1625#[serde(rename_all = "snake_case")]
1626pub enum SlackTableColumnAlign {
1627 Left,
1628 Center,
1629 Right,
1630}
1631
1632#[skip_serializing_none]
1636#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1637pub struct SlackTaskCardBlock {
1638 pub task_id: SlackTaskId,
1639 pub title: String,
1640 pub block_id: Option<SlackBlockId>,
1641 pub status: Option<SlackTaskCardStatus>,
1642 #[serde(rename = "details")]
1643 pub details: Option<SlackRichTextInlineContent>,
1644 #[serde(rename = "output")]
1645 pub output: Option<SlackRichTextInlineContent>,
1646 pub sources: Option<Vec<SlackTaskCardSource>>,
1647}
1648
1649impl From<SlackTaskCardBlock> for SlackBlock {
1650 fn from(block: SlackTaskCardBlock) -> Self {
1651 SlackBlock::TaskCard(block)
1652 }
1653}
1654
1655#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1656#[serde(rename_all = "snake_case")]
1657pub enum SlackTaskCardStatus {
1658 Pending,
1659 InProgress,
1660 Complete,
1661 Error,
1662}
1663
1664#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1668pub struct SlackUrlSourceElement {
1669 pub url: Url,
1670 pub text: String,
1671}
1672
1673#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1674#[serde(tag = "type")]
1675pub enum SlackTaskCardSource {
1676 #[serde(rename = "url")]
1677 Url(SlackUrlSourceElement),
1678}
1679
1680impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1681 fn from(element: SlackUrlSourceElement) -> Self {
1682 SlackTaskCardSource::Url(element)
1683 }
1684}
1685
1686#[skip_serializing_none]
1690#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1691pub struct SlackBlockFileInputElement {
1692 pub action_id: SlackActionId,
1693 pub filetypes: Option<Vec<String>>,
1694 pub max_files: Option<u64>,
1695}
1696
1697impl From<SlackBlockFileInputElement> for SlackInputBlockElement {
1698 fn from(element: SlackBlockFileInputElement) -> Self {
1699 SlackInputBlockElement::FileInput(element)
1700 }
1701}
1702
1703#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1707#[serde(rename_all = "snake_case")]
1708pub enum SlackAlertLevel {
1709 Warning,
1710 Error,
1711 Info,
1712 Success,
1713}
1714
1715#[skip_serializing_none]
1716#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1717pub struct SlackAlertBlock {
1718 pub block_id: Option<SlackBlockId>,
1719 pub text: SlackBlockText,
1720 pub level: Option<SlackAlertLevel>,
1721}
1722
1723impl From<SlackAlertBlock> for SlackBlock {
1724 fn from(block: SlackAlertBlock) -> Self {
1725 SlackBlock::Alert(block)
1726 }
1727}
1728
1729#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1733#[serde(tag = "type")]
1734pub enum SlackCardImageElement {
1735 #[serde(rename = "image")]
1736 Image(SlackBlockImageElement),
1737}
1738
1739impl From<SlackBlockImageElement> for SlackCardImageElement {
1740 fn from(element: SlackBlockImageElement) -> Self {
1741 SlackCardImageElement::Image(element)
1742 }
1743}
1744
1745#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1746#[serde(tag = "type")]
1747pub enum SlackCardActionBlockElement {
1748 #[serde(rename = "button")]
1749 Button(SlackBlockButtonElement),
1750}
1751
1752impl From<SlackBlockButtonElement> for SlackCardActionBlockElement {
1753 fn from(element: SlackBlockButtonElement) -> Self {
1754 SlackCardActionBlockElement::Button(element)
1755 }
1756}
1757
1758#[skip_serializing_none]
1759#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1760pub struct SlackCardBlock {
1761 pub block_id: Option<SlackBlockId>,
1762 pub title: Option<SlackBlockText>,
1763 pub subtitle: Option<SlackBlockText>,
1764 pub body: Option<SlackBlockText>,
1765 pub hero_image: Option<SlackCardImageElement>,
1766 pub icon: Option<SlackCardImageElement>,
1767 pub actions: Option<Vec<SlackCardActionBlockElement>>,
1768}
1769
1770impl From<SlackCardBlock> for SlackBlock {
1771 fn from(block: SlackCardBlock) -> Self {
1772 SlackBlock::Card(block)
1773 }
1774}
1775
1776#[skip_serializing_none]
1780#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1781pub struct SlackCarouselBlock {
1782 pub block_id: Option<SlackBlockId>,
1783 pub elements: Vec<SlackBlock>,
1784}
1785
1786impl From<SlackCarouselBlock> for SlackBlock {
1787 fn from(block: SlackCarouselBlock) -> Self {
1788 SlackBlock::Carousel(block)
1789 }
1790}
1791
1792#[skip_serializing_none]
1798#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1799pub struct SlackFeedbackButtonItem {
1800 pub action_id: SlackActionId,
1801 pub value: String,
1802 pub text: SlackBlockPlainTextOnly,
1803 pub confirm: Option<SlackBlockConfirmItem>,
1804}
1805
1806#[skip_serializing_none]
1807#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1808pub struct SlackBlockFeedbackButtonsElement {
1809 pub action_id: SlackActionId,
1810 pub positive: SlackFeedbackButtonItem,
1811 pub negative: SlackFeedbackButtonItem,
1812}
1813
1814#[skip_serializing_none]
1815#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1816pub struct SlackBlockIconButtonElement {
1817 pub action_id: SlackActionId,
1818 pub icon: String,
1819 pub text: SlackBlockPlainTextOnly,
1820 pub value: Option<String>,
1821 pub confirm: Option<SlackBlockConfirmItem>,
1822 pub accessibility_label: Option<SlackAccessibilityLabel>,
1823 pub visible_to_user_ids: Option<Vec<SlackUserId>>,
1824}
1825
1826#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1827#[serde(tag = "type")]
1828pub enum SlackContextActionBlockElement {
1829 #[serde(rename = "feedback_buttons")]
1830 FeedbackButtons(SlackBlockFeedbackButtonsElement),
1831 #[serde(rename = "icon_button")]
1832 IconButton(SlackBlockIconButtonElement),
1833}
1834
1835impl From<SlackBlockFeedbackButtonsElement> for SlackContextActionBlockElement {
1836 fn from(element: SlackBlockFeedbackButtonsElement) -> Self {
1837 SlackContextActionBlockElement::FeedbackButtons(element)
1838 }
1839}
1840
1841impl From<SlackBlockIconButtonElement> for SlackContextActionBlockElement {
1842 fn from(element: SlackBlockIconButtonElement) -> Self {
1843 SlackContextActionBlockElement::IconButton(element)
1844 }
1845}
1846
1847#[skip_serializing_none]
1848#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1849pub struct SlackContextActionsBlock {
1850 pub block_id: Option<SlackBlockId>,
1851 pub elements: Vec<SlackContextActionBlockElement>,
1852}
1853
1854impl From<SlackContextActionsBlock> for SlackBlock {
1855 fn from(block: SlackContextActionsBlock) -> Self {
1856 SlackBlock::ContextActions(block)
1857 }
1858}
1859
1860#[cfg(test)]
1861mod test {
1862 use super::*;
1863 use crate::blocks::SlackHomeView;
1864
1865 #[test]
1866 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1867 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1868 let block: SlackBlock = serde_json::from_str(payload)?;
1869 match block {
1870 SlackBlock::Section(section) => match section.accessory {
1871 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1872 let filter = elem.filter.expect("filter should be present");
1873 let include = filter.include.expect("include should be present");
1874 assert_eq!(include.len(), 2);
1875 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1876 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1877 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1878 assert_eq!(filter.exclude_bot_users, Some(true));
1879 }
1880 _ => panic!("Expected ConversationsSelect accessory"),
1881 },
1882 _ => panic!("Expected Section block"),
1883 }
1884 Ok(())
1885 }
1886
1887 #[test]
1888 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1889 let filter = SlackBlockConversationFilter::new()
1890 .with_include(vec![
1891 SlackConversationFilterInclude::Im,
1892 SlackConversationFilterInclude::Mpim,
1893 ])
1894 .with_exclude_bot_users(true);
1895
1896 let json = serde_json::to_value(&filter)?;
1897 assert_eq!(
1898 json,
1899 serde_json::json!({
1900 "include": ["im", "mpim"],
1901 "exclude_bot_users": true
1902 })
1903 );
1904 Ok(())
1905 }
1906
1907 #[test]
1908 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1909 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1910 .with_filter(
1911 SlackBlockConversationFilter::new()
1912 .with_include(vec![SlackConversationFilterInclude::Public])
1913 .with_exclude_external_shared_channels(true),
1914 );
1915
1916 let json = serde_json::to_string(&elem)?;
1917 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1918 assert_eq!(elem, parsed);
1919 Ok(())
1920 }
1921
1922 #[test]
1923 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1924 let elem =
1925 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1926 .with_filter(
1927 SlackBlockConversationFilter::new()
1928 .with_include(vec![
1929 SlackConversationFilterInclude::Public,
1930 SlackConversationFilterInclude::Private,
1931 ])
1932 .with_exclude_bot_users(true),
1933 );
1934
1935 let json = serde_json::to_string(&elem)?;
1936 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1937 assert_eq!(elem, parsed);
1938 Ok(())
1939 }
1940
1941 #[test]
1942 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1943 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1944
1945 let json = serde_json::to_value(&elem)?;
1946 assert!(json.get("filter").is_none());
1947 Ok(())
1948 }
1949
1950 #[test]
1951 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1952 let payload = include_str!("./fixtures/slack_image_blocks.json");
1953 let content: SlackMessageContent = serde_json::from_str(payload)?;
1954 let blocks = content.blocks.expect("Blocks should not be empty");
1955 match blocks.first() {
1956 Some(SlackBlock::Section(section)) => match §ion.accessory {
1957 Some(SlackSectionBlockElement::Image(image)) => {
1958 assert_eq!(image.alt_text, "alt text for image");
1959 match &image.image_url_or_file {
1960 SlackImageUrlOrFile::ImageUrl { image_url } => {
1961 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1962 }
1963 SlackImageUrlOrFile::SlackFile { slack_file } => {
1964 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1965 }
1966 }
1967 }
1968 _ => panic!("Expected a section block with an image accessory"),
1969 },
1970 _ => panic!("Expected a section block"),
1971 }
1972 Ok(())
1973 }
1974
1975 #[test]
1976 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1977 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1978 let block: SlackBlock = serde_json::from_str(payload)?;
1979
1980 let rich = match block {
1981 SlackBlock::RichText(r) => r,
1982 _ => panic!("Expected a RichText block"),
1983 };
1984
1985 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1986 assert_eq!(rich.elements.len(), 4);
1987
1988 let section = match &rich.elements[0] {
1990 SlackRichTextElement::Section(s) => s,
1991 _ => panic!("Expected a Section element"),
1992 };
1993 assert_eq!(section.elements.len(), 7);
1994
1995 let text = match §ion.elements[0] {
1997 SlackRichTextInlineElement::Text(t) => t,
1998 _ => panic!("Expected a Text element"),
1999 };
2000 assert_eq!(text.text, "Hello ");
2001 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
2002
2003 assert!(matches!(
2005 §ion.elements[1],
2006 SlackRichTextInlineElement::User(_)
2007 ));
2008
2009 let emoji = match §ion.elements[4] {
2011 SlackRichTextInlineElement::Emoji(e) => e,
2012 _ => panic!("Expected an Emoji element"),
2013 };
2014 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
2015
2016 let list = match &rich.elements[1] {
2018 SlackRichTextElement::List(l) => l,
2019 _ => panic!("Expected a List element"),
2020 };
2021 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
2022 assert_eq!(list.elements.len(), 2);
2023
2024 assert!(matches!(
2026 &list.elements[0],
2027 SlackRichTextListElement::Section(_)
2028 ));
2029 assert!(matches!(
2030 &list.elements[1],
2031 SlackRichTextListElement::Section(_)
2032 ));
2033
2034 assert!(matches!(
2036 &rich.elements[2],
2037 SlackRichTextElement::Preformatted(_)
2038 ));
2039
2040 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
2042
2043 Ok(())
2044 }
2045
2046 #[test]
2047 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2048 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2049 let block: SlackBlock = serde_json::from_str(payload)?;
2050 let serialized = serde_json::to_string(&block)?;
2051 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2052 assert_eq!(block, block2);
2053 Ok(())
2054 }
2055
2056 #[test]
2057 fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2058 let payload = include_str!("./fixtures/slack_table_block.json");
2059 let block: SlackBlock = serde_json::from_str(payload)?;
2060
2061 let table = match block {
2062 SlackBlock::Table(t) => t,
2063 _ => panic!("Expected a Table block"),
2064 };
2065
2066 assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
2067 assert_eq!(table.rows.len(), 2);
2068 assert_eq!(table.rows[0].len(), 2);
2069
2070 match &table.rows[0][0] {
2072 SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
2073 _ => panic!("Expected RawText cell"),
2074 }
2075
2076 match &table.rows[1][1] {
2078 SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
2079 _ => panic!("Expected RichText cell"),
2080 }
2081
2082 let settings = table
2083 .column_settings
2084 .expect("column_settings should be present");
2085 assert_eq!(settings.len(), 2);
2086 assert_eq!(settings[0].is_wrapped, Some(true));
2087 assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
2088
2089 Ok(())
2090 }
2091
2092 #[test]
2093 fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2094 let payload = include_str!("./fixtures/slack_table_block.json");
2095 let block: SlackBlock = serde_json::from_str(payload)?;
2096 let serialized = serde_json::to_string(&block)?;
2097 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2098 assert_eq!(block, block2);
2099 Ok(())
2100 }
2101
2102 #[test]
2103 fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2104 let payload = include_str!("./fixtures/slack_task_card_block.json");
2105 let block: SlackBlock = serde_json::from_str(payload)?;
2106
2107 let task_card = match block {
2108 SlackBlock::TaskCard(t) => t,
2109 _ => panic!("Expected a TaskCard block"),
2110 };
2111
2112 assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
2113 assert_eq!(task_card.title, "Fetching weather data");
2114 assert_eq!(
2115 task_card.block_id,
2116 Some(SlackBlockId("task_card_block_1".into()))
2117 );
2118 assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
2119
2120 let output = task_card.output.expect("output should be present");
2121 let SlackRichTextInlineContent::RichText(output_block) = output;
2122 assert_eq!(output_block.elements.len(), 1);
2123
2124 let sources = task_card.sources.expect("sources should be present");
2125 assert_eq!(sources.len(), 2);
2126 match &sources[0] {
2127 SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
2128 }
2129
2130 Ok(())
2131 }
2132
2133 #[test]
2134 fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2135 let payload = include_str!("./fixtures/slack_task_card_block.json");
2136 let block: SlackBlock = serde_json::from_str(payload)?;
2137 let serialized = serde_json::to_string(&block)?;
2138 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2139 assert_eq!(block, block2);
2140 Ok(())
2141 }
2142
2143 #[test]
2144 fn test_slack_alert_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2145 let payload = include_str!("./fixtures/slack_alert_block.json");
2146 let block: SlackBlock = serde_json::from_str(payload)?;
2147 match block {
2148 SlackBlock::Alert(alert) => {
2149 assert_eq!(alert.level, Some(SlackAlertLevel::Warning));
2150 }
2151 _ => panic!("Expected Alert block"),
2152 }
2153 Ok(())
2154 }
2155
2156 #[test]
2157 fn test_slack_alert_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2158 let payload = include_str!("./fixtures/slack_alert_block.json");
2159 let block: SlackBlock = serde_json::from_str(payload)?;
2160 let serialized = serde_json::to_string(&block)?;
2161 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2162 assert_eq!(block, block2);
2163 Ok(())
2164 }
2165
2166 #[test]
2167 fn test_slack_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2168 let payload = include_str!("./fixtures/slack_card_block.json");
2169 let block: SlackBlock = serde_json::from_str(payload)?;
2170 match block {
2171 SlackBlock::Card(card) => {
2172 assert!(card.hero_image.is_some());
2173 assert!(card.actions.is_some());
2174 let actions = card.actions.unwrap();
2175 assert_eq!(actions.len(), 1);
2176 match &actions[0] {
2177 SlackCardActionBlockElement::Button(btn) => {
2178 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2179 }
2180 }
2181 }
2182 _ => panic!("Expected Card block"),
2183 }
2184 Ok(())
2185 }
2186
2187 #[test]
2188 fn test_slack_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2189 let payload = include_str!("./fixtures/slack_card_block.json");
2190 let block: SlackBlock = serde_json::from_str(payload)?;
2191 let serialized = serde_json::to_string(&block)?;
2192 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2193 assert_eq!(block, block2);
2194 Ok(())
2195 }
2196
2197 #[test]
2198 fn test_slack_context_actions_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2199 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2200 let block: SlackBlock = serde_json::from_str(payload)?;
2201 match block {
2202 SlackBlock::ContextActions(ctx) => {
2203 assert_eq!(ctx.elements.len(), 1);
2204 match &ctx.elements[0] {
2205 SlackContextActionBlockElement::IconButton(btn) => {
2206 assert_eq!(btn.icon, "trash");
2207 }
2208 _ => panic!("Expected IconButton element"),
2209 }
2210 }
2211 _ => panic!("Expected ContextActions block"),
2212 }
2213 Ok(())
2214 }
2215
2216 #[test]
2217 fn test_slack_context_actions_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2218 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2219 let block: SlackBlock = serde_json::from_str(payload)?;
2220 let serialized = serde_json::to_string(&block)?;
2221 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2222 assert_eq!(block, block2);
2223 Ok(())
2224 }
2225
2226 #[test]
2227 fn test_slack_workflow_button_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2228 let payload = include_str!("./fixtures/slack_workflow_button.json");
2229 let block: SlackBlock = serde_json::from_str(payload)?;
2230 match block {
2231 SlackBlock::Actions(actions) => {
2232 assert_eq!(actions.elements.len(), 1);
2233 match &actions.elements[0] {
2234 SlackActionBlockElement::WorkflowButton(btn) => {
2235 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2236 let params = btn
2237 .workflow
2238 .trigger
2239 .customizable_input_parameters
2240 .as_ref()
2241 .expect("params should be present");
2242 assert_eq!(params.len(), 1);
2243 assert_eq!(params[0].name, "user_input");
2244 }
2245 _ => panic!("Expected WorkflowButton element"),
2246 }
2247 }
2248 _ => panic!("Expected Actions block"),
2249 }
2250 Ok(())
2251 }
2252
2253 #[test]
2254 fn test_slack_workflow_button_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2255 let payload = include_str!("./fixtures/slack_workflow_button.json");
2256 let block: SlackBlock = serde_json::from_str(payload)?;
2257 let serialized = serde_json::to_string(&block)?;
2258 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2259 assert_eq!(block, block2);
2260 Ok(())
2261 }
2262
2263 #[test]
2264 fn test_rich_text_message_mention_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2265 let payload = serde_json::json!({
2266 "type": "rich_text",
2267 "block_id": "msgm1",
2268 "elements": [
2269 {
2270 "type": "rich_text_section",
2271 "elements": [
2272 {
2273 "type": "message_mention",
2274 "url": "https://acme.slack.com/archives/C12345678/p1784153496441789?thread_ts=1784153496.441789&cid=C12345678",
2275 "text": "a message",
2276 "channel_id": "C12345678",
2277 "author_id": "U12345678",
2278 "message_ts": "1784153496.441789",
2279 "thread_ts": "1784153496.441789"
2280 }
2281 ]
2282 }
2283 ]
2284 })
2285 .to_string();
2286 let block: SlackBlock = serde_json::from_str(&payload)?;
2287 match block {
2288 SlackBlock::RichText(rich_text) => match &rich_text.elements[0] {
2289 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2290 SlackRichTextInlineElement::MessageMention(mention) => {
2291 assert_eq!(mention.channel_id, Some(SlackChannelId("C12345678".into())));
2292 assert_eq!(mention.author_id, Some(SlackUserId("U12345678".into())));
2293 assert_eq!(
2294 mention.message_ts,
2295 Some(SlackTs("1784153496.441789".into()))
2296 );
2297 }
2298 other => panic!("Expected MessageMention element, got {other:?}"),
2299 },
2300 _ => panic!("Expected Section element"),
2301 },
2302 _ => panic!("Expected RichText block"),
2303 }
2304 Ok(())
2305 }
2306
2307 #[test]
2308 fn test_rich_text_unknown_inline_element_deserialize() -> Result<(), Box<dyn std::error::Error>>
2309 {
2310 let payload = serde_json::json!({
2311 "type": "rich_text_section",
2312 "elements": [
2313 {
2314 "type": "some_future_element",
2315 "foo": "bar"
2316 }
2317 ]
2318 })
2319 .to_string();
2320 let section: SlackRichTextElement = serde_json::from_str(&payload)?;
2321 match section {
2322 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2323 SlackRichTextInlineElement::Unknown(value) => {
2324 assert_eq!(value["type"], "some_future_element");
2325 }
2326 other => panic!("Expected Unknown element, got {other:?}"),
2327 },
2328 _ => panic!("Expected Section element"),
2329 }
2330 Ok(())
2331 }
2332
2333 #[test]
2334 fn rich_text_str_converts_to_text_inline_element() -> Result<(), Box<dyn std::error::Error>> {
2335 let element: SlackRichTextInlineElement = "hi".into();
2336 assert_eq!(
2337 serde_json::to_value(&element)?,
2338 serde_json::json!({"type": "text", "text": "hi"})
2339 );
2340
2341 let owned: SlackRichTextInlineElement = "hi".to_string().into();
2342 assert_eq!(
2343 serde_json::to_value(&owned)?,
2344 serde_json::to_value(&element)?
2345 );
2346 Ok(())
2347 }
2348
2349 #[test]
2350 fn rich_text_leaf_elements_convert_to_inline_elements() -> Result<(), Box<dyn std::error::Error>>
2351 {
2352 let text: SlackRichTextInlineElement = SlackRichTextText::new("t".to_string()).into();
2353 assert_eq!(
2354 serde_json::to_value(&text)?,
2355 serde_json::json!({"type": "text", "text": "t"})
2356 );
2357
2358 let link: SlackRichTextInlineElement =
2359 SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into())).into();
2360 assert_eq!(
2361 serde_json::to_value(&link)?,
2362 serde_json::json!({"type": "link", "url": "https://example.com"})
2363 );
2364
2365 let user: SlackRichTextInlineElement =
2366 SlackRichTextUser::new(SlackUserId("U1".into())).into();
2367 assert_eq!(
2368 serde_json::to_value(&user)?,
2369 serde_json::json!({"type": "user", "user_id": "U1"})
2370 );
2371
2372 let channel: SlackRichTextInlineElement =
2373 SlackRichTextChannel::new(SlackChannelId("C1".into())).into();
2374 assert_eq!(
2375 serde_json::to_value(&channel)?,
2376 serde_json::json!({"type": "channel", "channel_id": "C1"})
2377 );
2378
2379 let usergroup: SlackRichTextInlineElement =
2380 SlackRichTextUserGroup::new(SlackUserGroupId("G1".into())).into();
2381 assert_eq!(
2382 serde_json::to_value(&usergroup)?,
2383 serde_json::json!({"type": "usergroup", "usergroup_id": "G1"})
2384 );
2385
2386 let emoji: SlackRichTextInlineElement =
2387 SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into();
2388 assert_eq!(
2389 serde_json::to_value(&emoji)?,
2390 serde_json::json!({"type": "emoji", "name": "wave"})
2391 );
2392
2393 let date: SlackRichTextInlineElement = SlackRichTextDate::new(
2394 SlackDateTime("2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>()?),
2395 "{date_short}".to_string(),
2396 )
2397 .into();
2398 assert_eq!(
2399 serde_json::to_value(&date)?,
2400 serde_json::json!({"type": "date", "timestamp": 1_577_839_362_i64, "format": "{date_short}"})
2401 );
2402
2403 let broadcast: SlackRichTextInlineElement =
2404 SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into();
2405 assert_eq!(
2406 serde_json::to_value(&broadcast)?,
2407 serde_json::json!({"type": "broadcast", "range": "here"})
2408 );
2409
2410 let color: SlackRichTextInlineElement =
2411 SlackRichTextColor::new("#ff0000".to_string()).into();
2412 assert_eq!(
2413 serde_json::to_value(&color)?,
2414 serde_json::json!({"type": "color", "value": "#ff0000"})
2415 );
2416
2417 let mention: SlackRichTextInlineElement = SlackRichTextMessageMention::new(
2418 SlackRelaxedUrl("https://example.com/archives/C1/p1".into()),
2419 )
2420 .into();
2421 assert_eq!(
2422 serde_json::to_value(&mention)?,
2423 serde_json::json!({"type": "message_mention", "url": "https://example.com/archives/C1/p1"})
2424 );
2425
2426 Ok(())
2427 }
2428
2429 #[test]
2430 fn rich_text_list_accepts_str_items() -> Result<(), Box<dyn std::error::Error>> {
2431 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2432 let block: SlackBlock = serde_json::from_str(payload)?;
2433 let expected_list = match block {
2434 SlackBlock::RichText(r) => match &r.elements[1] {
2435 SlackRichTextElement::List(l) => l.clone(),
2436 other => panic!("Expected List element, got {other:?}"),
2437 },
2438 _ => panic!("Expected RichText block"),
2439 };
2440
2441 let list = SlackRichTextList::new(
2442 SlackRichTextListStyle::Bullet,
2443 vec!["Item one".into(), "Item two".into()],
2444 )
2445 .with_indent(0);
2446
2447 assert_eq!(list, expected_list);
2448 Ok(())
2449 }
2450
2451 #[test]
2452 fn rich_text_style_helpers_merge_flags() -> Result<(), Box<dyn std::error::Error>> {
2453 let bold = SlackRichTextText::new("hi".to_string()).bold();
2454 let json = serde_json::to_value(&bold)?;
2455 assert_eq!(json["style"], serde_json::json!({"bold": true}));
2456
2457 let both = SlackRichTextText::new("hi".to_string())
2458 .with_style(SlackRichTextStyle::new().with_italic(true))
2459 .bold();
2460 let json2 = serde_json::to_value(&both)?;
2461 assert_eq!(
2462 json2["style"],
2463 serde_json::json!({"bold": true, "italic": true})
2464 );
2465
2466 let struck = SlackRichTextText::new("hi".to_string()).strike();
2467 assert_eq!(
2468 serde_json::to_value(&struck)?["style"],
2469 serde_json::json!({"strike": true})
2470 );
2471
2472 let coded = SlackRichTextText::new("hi".to_string()).code();
2473 assert_eq!(
2474 serde_json::to_value(&coded)?["style"],
2475 serde_json::json!({"code": true})
2476 );
2477
2478 Ok(())
2479 }
2480
2481 #[test]
2482 fn table_block_builds_from_str_cells() -> Result<(), Box<dyn std::error::Error>> {
2483 let payload = include_str!("./fixtures/slack_table_block.json");
2484 let expected: serde_json::Value = serde_json::from_str(payload)?;
2485
2486 let block: SlackBlock = SlackTableBlock::new(vec![
2487 vec!["Header A".into(), "Header B".into()],
2488 vec![
2489 "Data 1A".into(),
2490 SlackTableRichTextCell::new(vec![SlackRichTextSection::new(vec![
2491 SlackRichTextLink::new(SlackRelaxedUrl("https://slack.com".into()))
2492 .with_text("Data 1B".to_string())
2493 .into(),
2494 ])
2495 .into()])
2496 .into(),
2497 ],
2498 ])
2499 .with_block_id(SlackBlockId("table_block_1".into()))
2500 .with_column_settings(vec![
2501 SlackTableColumnSetting::new().with_is_wrapped(true),
2502 SlackTableColumnSetting::new().with_align(SlackTableColumnAlign::Right),
2503 ])
2504 .into();
2505
2506 assert_eq!(serde_json::to_value(&block)?, expected);
2507 Ok(())
2508 }
2509
2510 #[test]
2511 fn rich_text_block_builds_fixture_with_conversions() -> Result<(), Box<dyn std::error::Error>> {
2512 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2513 let expected: serde_json::Value = serde_json::from_str(payload)?;
2514
2515 let section: SlackRichTextElement = SlackRichTextSection::new(vec![
2516 SlackRichTextText::new("Hello ".to_string()).bold().into(),
2517 SlackRichTextUser::new(SlackUserId("U123ABC456".into())).into(),
2518 "! Check out ".into(),
2519 SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into()))
2520 .with_text("this link".to_string())
2521 .with_style(SlackRichTextStyle::new().with_italic(true))
2522 .into(),
2523 SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into(),
2524 SlackRichTextChannel::new(SlackChannelId("C123ABC456".into())).into(),
2525 SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into(),
2526 ])
2527 .into();
2528
2529 let list: SlackRichTextElement = SlackRichTextList::new(
2530 SlackRichTextListStyle::Bullet,
2531 vec!["Item one".into(), "Item two".into()],
2532 )
2533 .with_indent(0)
2534 .into();
2535
2536 let preformatted: SlackRichTextElement =
2537 SlackRichTextPreformatted::new(vec![SlackRichTextText::new(
2538 "fn main() {}\n".to_string(),
2539 )
2540 .into()])
2541 .with_border(1)
2542 .into();
2543
2544 let quote: SlackRichTextElement =
2545 SlackRichTextQuote::new(vec![SlackRichTextText::new("A wise quote".to_string())
2546 .italic()
2547 .into()])
2548 .into();
2549
2550 let block: SlackBlock = SlackRichTextBlock::new(vec![section, list, preformatted, quote])
2551 .with_block_id(SlackBlockId("test_block".into()))
2552 .into();
2553
2554 assert_eq!(serde_json::to_value(&block)?, expected);
2555 Ok(())
2556 }
2557}