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 pub icon: Option<SlackTaskCardIcon>,
1643 pub hide_title: Option<bool>,
1644 #[serde(rename = "details")]
1645 pub details: Option<SlackRichTextInlineContent>,
1646 #[serde(rename = "output")]
1647 pub output: Option<SlackRichTextInlineContent>,
1648 pub sources: Option<Vec<SlackTaskCardSource>>,
1649}
1650
1651impl From<SlackTaskCardBlock> for SlackBlock {
1652 fn from(block: SlackTaskCardBlock) -> Self {
1653 SlackBlock::TaskCard(block)
1654 }
1655}
1656
1657#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1658#[serde(rename_all = "snake_case")]
1659pub enum SlackTaskCardStatus {
1660 Pending,
1661 InProgress,
1662 Complete,
1663 Error,
1664}
1665
1666#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1669#[serde(tag = "type", rename = "icon")]
1670pub struct SlackTaskCardIcon {
1671 pub name: String,
1672}
1673
1674#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1678pub struct SlackUrlSourceElement {
1679 pub url: Url,
1680 pub text: String,
1681}
1682
1683#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1684#[serde(tag = "type")]
1685pub enum SlackTaskCardSource {
1686 #[serde(rename = "url")]
1687 Url(SlackUrlSourceElement),
1688}
1689
1690impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1691 fn from(element: SlackUrlSourceElement) -> Self {
1692 SlackTaskCardSource::Url(element)
1693 }
1694}
1695
1696#[skip_serializing_none]
1700#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1701pub struct SlackBlockFileInputElement {
1702 pub action_id: SlackActionId,
1703 pub filetypes: Option<Vec<String>>,
1704 pub max_files: Option<u64>,
1705}
1706
1707impl From<SlackBlockFileInputElement> for SlackInputBlockElement {
1708 fn from(element: SlackBlockFileInputElement) -> Self {
1709 SlackInputBlockElement::FileInput(element)
1710 }
1711}
1712
1713#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1717#[serde(rename_all = "snake_case")]
1718pub enum SlackAlertLevel {
1719 Warning,
1720 Error,
1721 Info,
1722 Success,
1723}
1724
1725#[skip_serializing_none]
1726#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1727pub struct SlackAlertBlock {
1728 pub block_id: Option<SlackBlockId>,
1729 pub text: SlackBlockText,
1730 pub level: Option<SlackAlertLevel>,
1731}
1732
1733impl From<SlackAlertBlock> for SlackBlock {
1734 fn from(block: SlackAlertBlock) -> Self {
1735 SlackBlock::Alert(block)
1736 }
1737}
1738
1739#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1743#[serde(tag = "type")]
1744pub enum SlackCardImageElement {
1745 #[serde(rename = "image")]
1746 Image(SlackBlockImageElement),
1747}
1748
1749impl From<SlackBlockImageElement> for SlackCardImageElement {
1750 fn from(element: SlackBlockImageElement) -> Self {
1751 SlackCardImageElement::Image(element)
1752 }
1753}
1754
1755#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1756#[serde(tag = "type")]
1757pub enum SlackCardActionBlockElement {
1758 #[serde(rename = "button")]
1759 Button(SlackBlockButtonElement),
1760}
1761
1762impl From<SlackBlockButtonElement> for SlackCardActionBlockElement {
1763 fn from(element: SlackBlockButtonElement) -> Self {
1764 SlackCardActionBlockElement::Button(element)
1765 }
1766}
1767
1768#[skip_serializing_none]
1769#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1770pub struct SlackCardBlock {
1771 pub block_id: Option<SlackBlockId>,
1772 pub title: Option<SlackBlockText>,
1773 pub subtitle: Option<SlackBlockText>,
1774 pub body: Option<SlackBlockText>,
1775 pub hero_image: Option<SlackCardImageElement>,
1776 pub icon: Option<SlackCardImageElement>,
1777 pub actions: Option<Vec<SlackCardActionBlockElement>>,
1778}
1779
1780impl From<SlackCardBlock> for SlackBlock {
1781 fn from(block: SlackCardBlock) -> Self {
1782 SlackBlock::Card(block)
1783 }
1784}
1785
1786#[skip_serializing_none]
1790#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1791pub struct SlackCarouselBlock {
1792 pub block_id: Option<SlackBlockId>,
1793 pub elements: Vec<SlackBlock>,
1794}
1795
1796impl From<SlackCarouselBlock> for SlackBlock {
1797 fn from(block: SlackCarouselBlock) -> Self {
1798 SlackBlock::Carousel(block)
1799 }
1800}
1801
1802#[skip_serializing_none]
1808#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1809pub struct SlackFeedbackButtonItem {
1810 pub action_id: SlackActionId,
1811 pub value: String,
1812 pub text: SlackBlockPlainTextOnly,
1813 pub confirm: Option<SlackBlockConfirmItem>,
1814}
1815
1816#[skip_serializing_none]
1817#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1818pub struct SlackBlockFeedbackButtonsElement {
1819 pub action_id: SlackActionId,
1820 pub positive: SlackFeedbackButtonItem,
1821 pub negative: SlackFeedbackButtonItem,
1822}
1823
1824#[skip_serializing_none]
1825#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1826pub struct SlackBlockIconButtonElement {
1827 pub action_id: SlackActionId,
1828 pub icon: String,
1829 pub text: SlackBlockPlainTextOnly,
1830 pub value: Option<String>,
1831 pub confirm: Option<SlackBlockConfirmItem>,
1832 pub accessibility_label: Option<SlackAccessibilityLabel>,
1833 pub visible_to_user_ids: Option<Vec<SlackUserId>>,
1834}
1835
1836#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1837#[serde(tag = "type")]
1838pub enum SlackContextActionBlockElement {
1839 #[serde(rename = "feedback_buttons")]
1840 FeedbackButtons(SlackBlockFeedbackButtonsElement),
1841 #[serde(rename = "icon_button")]
1842 IconButton(SlackBlockIconButtonElement),
1843}
1844
1845impl From<SlackBlockFeedbackButtonsElement> for SlackContextActionBlockElement {
1846 fn from(element: SlackBlockFeedbackButtonsElement) -> Self {
1847 SlackContextActionBlockElement::FeedbackButtons(element)
1848 }
1849}
1850
1851impl From<SlackBlockIconButtonElement> for SlackContextActionBlockElement {
1852 fn from(element: SlackBlockIconButtonElement) -> Self {
1853 SlackContextActionBlockElement::IconButton(element)
1854 }
1855}
1856
1857#[skip_serializing_none]
1858#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1859pub struct SlackContextActionsBlock {
1860 pub block_id: Option<SlackBlockId>,
1861 pub elements: Vec<SlackContextActionBlockElement>,
1862}
1863
1864impl From<SlackContextActionsBlock> for SlackBlock {
1865 fn from(block: SlackContextActionsBlock) -> Self {
1866 SlackBlock::ContextActions(block)
1867 }
1868}
1869
1870#[cfg(test)]
1871mod test {
1872 use super::*;
1873 use crate::blocks::SlackHomeView;
1874
1875 #[test]
1876 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1877 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1878 let block: SlackBlock = serde_json::from_str(payload)?;
1879 match block {
1880 SlackBlock::Section(section) => match section.accessory {
1881 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1882 let filter = elem.filter.expect("filter should be present");
1883 let include = filter.include.expect("include should be present");
1884 assert_eq!(include.len(), 2);
1885 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1886 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1887 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1888 assert_eq!(filter.exclude_bot_users, Some(true));
1889 }
1890 _ => panic!("Expected ConversationsSelect accessory"),
1891 },
1892 _ => panic!("Expected Section block"),
1893 }
1894 Ok(())
1895 }
1896
1897 #[test]
1898 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1899 let filter = SlackBlockConversationFilter::new()
1900 .with_include(vec![
1901 SlackConversationFilterInclude::Im,
1902 SlackConversationFilterInclude::Mpim,
1903 ])
1904 .with_exclude_bot_users(true);
1905
1906 let json = serde_json::to_value(&filter)?;
1907 assert_eq!(
1908 json,
1909 serde_json::json!({
1910 "include": ["im", "mpim"],
1911 "exclude_bot_users": true
1912 })
1913 );
1914 Ok(())
1915 }
1916
1917 #[test]
1918 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1919 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1920 .with_filter(
1921 SlackBlockConversationFilter::new()
1922 .with_include(vec![SlackConversationFilterInclude::Public])
1923 .with_exclude_external_shared_channels(true),
1924 );
1925
1926 let json = serde_json::to_string(&elem)?;
1927 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1928 assert_eq!(elem, parsed);
1929 Ok(())
1930 }
1931
1932 #[test]
1933 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1934 let elem =
1935 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1936 .with_filter(
1937 SlackBlockConversationFilter::new()
1938 .with_include(vec![
1939 SlackConversationFilterInclude::Public,
1940 SlackConversationFilterInclude::Private,
1941 ])
1942 .with_exclude_bot_users(true),
1943 );
1944
1945 let json = serde_json::to_string(&elem)?;
1946 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1947 assert_eq!(elem, parsed);
1948 Ok(())
1949 }
1950
1951 #[test]
1952 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1953 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1954
1955 let json = serde_json::to_value(&elem)?;
1956 assert!(json.get("filter").is_none());
1957 Ok(())
1958 }
1959
1960 #[test]
1961 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1962 let payload = include_str!("./fixtures/slack_image_blocks.json");
1963 let content: SlackMessageContent = serde_json::from_str(payload)?;
1964 let blocks = content.blocks.expect("Blocks should not be empty");
1965 match blocks.first() {
1966 Some(SlackBlock::Section(section)) => match §ion.accessory {
1967 Some(SlackSectionBlockElement::Image(image)) => {
1968 assert_eq!(image.alt_text, "alt text for image");
1969 match &image.image_url_or_file {
1970 SlackImageUrlOrFile::ImageUrl { image_url } => {
1971 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1972 }
1973 SlackImageUrlOrFile::SlackFile { slack_file } => {
1974 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1975 }
1976 }
1977 }
1978 _ => panic!("Expected a section block with an image accessory"),
1979 },
1980 _ => panic!("Expected a section block"),
1981 }
1982 Ok(())
1983 }
1984
1985 #[test]
1986 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1987 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1988 let block: SlackBlock = serde_json::from_str(payload)?;
1989
1990 let rich = match block {
1991 SlackBlock::RichText(r) => r,
1992 _ => panic!("Expected a RichText block"),
1993 };
1994
1995 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1996 assert_eq!(rich.elements.len(), 4);
1997
1998 let section = match &rich.elements[0] {
2000 SlackRichTextElement::Section(s) => s,
2001 _ => panic!("Expected a Section element"),
2002 };
2003 assert_eq!(section.elements.len(), 7);
2004
2005 let text = match §ion.elements[0] {
2007 SlackRichTextInlineElement::Text(t) => t,
2008 _ => panic!("Expected a Text element"),
2009 };
2010 assert_eq!(text.text, "Hello ");
2011 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
2012
2013 assert!(matches!(
2015 §ion.elements[1],
2016 SlackRichTextInlineElement::User(_)
2017 ));
2018
2019 let emoji = match §ion.elements[4] {
2021 SlackRichTextInlineElement::Emoji(e) => e,
2022 _ => panic!("Expected an Emoji element"),
2023 };
2024 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
2025
2026 let list = match &rich.elements[1] {
2028 SlackRichTextElement::List(l) => l,
2029 _ => panic!("Expected a List element"),
2030 };
2031 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
2032 assert_eq!(list.elements.len(), 2);
2033
2034 assert!(matches!(
2036 &list.elements[0],
2037 SlackRichTextListElement::Section(_)
2038 ));
2039 assert!(matches!(
2040 &list.elements[1],
2041 SlackRichTextListElement::Section(_)
2042 ));
2043
2044 assert!(matches!(
2046 &rich.elements[2],
2047 SlackRichTextElement::Preformatted(_)
2048 ));
2049
2050 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
2052
2053 Ok(())
2054 }
2055
2056 #[test]
2057 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2058 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2059 let block: SlackBlock = serde_json::from_str(payload)?;
2060 let serialized = serde_json::to_string(&block)?;
2061 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2062 assert_eq!(block, block2);
2063 Ok(())
2064 }
2065
2066 #[test]
2067 fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2068 let payload = include_str!("./fixtures/slack_table_block.json");
2069 let block: SlackBlock = serde_json::from_str(payload)?;
2070
2071 let table = match block {
2072 SlackBlock::Table(t) => t,
2073 _ => panic!("Expected a Table block"),
2074 };
2075
2076 assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
2077 assert_eq!(table.rows.len(), 2);
2078 assert_eq!(table.rows[0].len(), 2);
2079
2080 match &table.rows[0][0] {
2082 SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
2083 _ => panic!("Expected RawText cell"),
2084 }
2085
2086 match &table.rows[1][1] {
2088 SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
2089 _ => panic!("Expected RichText cell"),
2090 }
2091
2092 let settings = table
2093 .column_settings
2094 .expect("column_settings should be present");
2095 assert_eq!(settings.len(), 2);
2096 assert_eq!(settings[0].is_wrapped, Some(true));
2097 assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
2098
2099 Ok(())
2100 }
2101
2102 #[test]
2103 fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2104 let payload = include_str!("./fixtures/slack_table_block.json");
2105 let block: SlackBlock = serde_json::from_str(payload)?;
2106 let serialized = serde_json::to_string(&block)?;
2107 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2108 assert_eq!(block, block2);
2109 Ok(())
2110 }
2111
2112 #[test]
2113 fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2114 let payload = include_str!("./fixtures/slack_task_card_block.json");
2115 let block: SlackBlock = serde_json::from_str(payload)?;
2116
2117 let task_card = match block {
2118 SlackBlock::TaskCard(t) => t,
2119 _ => panic!("Expected a TaskCard block"),
2120 };
2121
2122 assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
2123 assert_eq!(task_card.title, "Fetching weather data");
2124 assert_eq!(
2125 task_card.block_id,
2126 Some(SlackBlockId("task_card_block_1".into()))
2127 );
2128 assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
2129
2130 let output = task_card.output.expect("output should be present");
2131 let SlackRichTextInlineContent::RichText(output_block) = output;
2132 assert_eq!(output_block.elements.len(), 1);
2133
2134 let sources = task_card.sources.expect("sources should be present");
2135 assert_eq!(sources.len(), 2);
2136 match &sources[0] {
2137 SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
2138 }
2139
2140 Ok(())
2141 }
2142
2143 #[test]
2144 fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2145 let payload = include_str!("./fixtures/slack_task_card_block.json");
2146 let block: SlackBlock = serde_json::from_str(payload)?;
2147 let serialized = serde_json::to_string(&block)?;
2148 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2149 assert_eq!(block, block2);
2150 Ok(())
2151 }
2152
2153 #[test]
2154 fn test_slack_task_card_block_serializes_icon_and_hide_title(
2155 ) -> Result<(), Box<dyn std::error::Error>> {
2156 let task_card = SlackTaskCardBlock::new(SlackTaskId("task_1".into()), "Title".into())
2157 .with_icon(SlackTaskCardIcon::new("check".into()))
2158 .with_hide_title(true);
2159 let json = serde_json::to_value(&task_card)?;
2160 assert_eq!(
2161 json["icon"],
2162 serde_json::json!({"type": "icon", "name": "check"})
2163 );
2164 assert_eq!(json["hide_title"], serde_json::json!(true));
2165 Ok(())
2166 }
2167
2168 #[test]
2169 fn test_slack_alert_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2170 let payload = include_str!("./fixtures/slack_alert_block.json");
2171 let block: SlackBlock = serde_json::from_str(payload)?;
2172 match block {
2173 SlackBlock::Alert(alert) => {
2174 assert_eq!(alert.level, Some(SlackAlertLevel::Warning));
2175 }
2176 _ => panic!("Expected Alert block"),
2177 }
2178 Ok(())
2179 }
2180
2181 #[test]
2182 fn test_slack_alert_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2183 let payload = include_str!("./fixtures/slack_alert_block.json");
2184 let block: SlackBlock = serde_json::from_str(payload)?;
2185 let serialized = serde_json::to_string(&block)?;
2186 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2187 assert_eq!(block, block2);
2188 Ok(())
2189 }
2190
2191 #[test]
2192 fn test_slack_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2193 let payload = include_str!("./fixtures/slack_card_block.json");
2194 let block: SlackBlock = serde_json::from_str(payload)?;
2195 match block {
2196 SlackBlock::Card(card) => {
2197 assert!(card.hero_image.is_some());
2198 assert!(card.actions.is_some());
2199 let actions = card.actions.unwrap();
2200 assert_eq!(actions.len(), 1);
2201 match &actions[0] {
2202 SlackCardActionBlockElement::Button(btn) => {
2203 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2204 }
2205 }
2206 }
2207 _ => panic!("Expected Card block"),
2208 }
2209 Ok(())
2210 }
2211
2212 #[test]
2213 fn test_slack_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2214 let payload = include_str!("./fixtures/slack_card_block.json");
2215 let block: SlackBlock = serde_json::from_str(payload)?;
2216 let serialized = serde_json::to_string(&block)?;
2217 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2218 assert_eq!(block, block2);
2219 Ok(())
2220 }
2221
2222 #[test]
2223 fn test_slack_context_actions_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2224 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2225 let block: SlackBlock = serde_json::from_str(payload)?;
2226 match block {
2227 SlackBlock::ContextActions(ctx) => {
2228 assert_eq!(ctx.elements.len(), 1);
2229 match &ctx.elements[0] {
2230 SlackContextActionBlockElement::IconButton(btn) => {
2231 assert_eq!(btn.icon, "trash");
2232 }
2233 _ => panic!("Expected IconButton element"),
2234 }
2235 }
2236 _ => panic!("Expected ContextActions block"),
2237 }
2238 Ok(())
2239 }
2240
2241 #[test]
2242 fn test_slack_context_actions_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2243 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2244 let block: SlackBlock = serde_json::from_str(payload)?;
2245 let serialized = serde_json::to_string(&block)?;
2246 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2247 assert_eq!(block, block2);
2248 Ok(())
2249 }
2250
2251 #[test]
2252 fn test_slack_workflow_button_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2253 let payload = include_str!("./fixtures/slack_workflow_button.json");
2254 let block: SlackBlock = serde_json::from_str(payload)?;
2255 match block {
2256 SlackBlock::Actions(actions) => {
2257 assert_eq!(actions.elements.len(), 1);
2258 match &actions.elements[0] {
2259 SlackActionBlockElement::WorkflowButton(btn) => {
2260 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2261 let params = btn
2262 .workflow
2263 .trigger
2264 .customizable_input_parameters
2265 .as_ref()
2266 .expect("params should be present");
2267 assert_eq!(params.len(), 1);
2268 assert_eq!(params[0].name, "user_input");
2269 }
2270 _ => panic!("Expected WorkflowButton element"),
2271 }
2272 }
2273 _ => panic!("Expected Actions block"),
2274 }
2275 Ok(())
2276 }
2277
2278 #[test]
2279 fn test_slack_workflow_button_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2280 let payload = include_str!("./fixtures/slack_workflow_button.json");
2281 let block: SlackBlock = serde_json::from_str(payload)?;
2282 let serialized = serde_json::to_string(&block)?;
2283 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2284 assert_eq!(block, block2);
2285 Ok(())
2286 }
2287
2288 #[test]
2289 fn test_rich_text_message_mention_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2290 let payload = serde_json::json!({
2291 "type": "rich_text",
2292 "block_id": "msgm1",
2293 "elements": [
2294 {
2295 "type": "rich_text_section",
2296 "elements": [
2297 {
2298 "type": "message_mention",
2299 "url": "https://acme.slack.com/archives/C12345678/p1784153496441789?thread_ts=1784153496.441789&cid=C12345678",
2300 "text": "a message",
2301 "channel_id": "C12345678",
2302 "author_id": "U12345678",
2303 "message_ts": "1784153496.441789",
2304 "thread_ts": "1784153496.441789"
2305 }
2306 ]
2307 }
2308 ]
2309 })
2310 .to_string();
2311 let block: SlackBlock = serde_json::from_str(&payload)?;
2312 match block {
2313 SlackBlock::RichText(rich_text) => match &rich_text.elements[0] {
2314 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2315 SlackRichTextInlineElement::MessageMention(mention) => {
2316 assert_eq!(mention.channel_id, Some(SlackChannelId("C12345678".into())));
2317 assert_eq!(mention.author_id, Some(SlackUserId("U12345678".into())));
2318 assert_eq!(
2319 mention.message_ts,
2320 Some(SlackTs("1784153496.441789".into()))
2321 );
2322 }
2323 other => panic!("Expected MessageMention element, got {other:?}"),
2324 },
2325 _ => panic!("Expected Section element"),
2326 },
2327 _ => panic!("Expected RichText block"),
2328 }
2329 Ok(())
2330 }
2331
2332 #[test]
2333 fn test_rich_text_unknown_inline_element_deserialize() -> Result<(), Box<dyn std::error::Error>>
2334 {
2335 let payload = serde_json::json!({
2336 "type": "rich_text_section",
2337 "elements": [
2338 {
2339 "type": "some_future_element",
2340 "foo": "bar"
2341 }
2342 ]
2343 })
2344 .to_string();
2345 let section: SlackRichTextElement = serde_json::from_str(&payload)?;
2346 match section {
2347 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2348 SlackRichTextInlineElement::Unknown(value) => {
2349 assert_eq!(value["type"], "some_future_element");
2350 }
2351 other => panic!("Expected Unknown element, got {other:?}"),
2352 },
2353 _ => panic!("Expected Section element"),
2354 }
2355 Ok(())
2356 }
2357
2358 #[test]
2359 fn rich_text_str_converts_to_text_inline_element() -> Result<(), Box<dyn std::error::Error>> {
2360 let element: SlackRichTextInlineElement = "hi".into();
2361 assert_eq!(
2362 serde_json::to_value(&element)?,
2363 serde_json::json!({"type": "text", "text": "hi"})
2364 );
2365
2366 let owned: SlackRichTextInlineElement = "hi".to_string().into();
2367 assert_eq!(
2368 serde_json::to_value(&owned)?,
2369 serde_json::to_value(&element)?
2370 );
2371 Ok(())
2372 }
2373
2374 #[test]
2375 fn rich_text_leaf_elements_convert_to_inline_elements() -> Result<(), Box<dyn std::error::Error>>
2376 {
2377 let text: SlackRichTextInlineElement = SlackRichTextText::new("t".to_string()).into();
2378 assert_eq!(
2379 serde_json::to_value(&text)?,
2380 serde_json::json!({"type": "text", "text": "t"})
2381 );
2382
2383 let link: SlackRichTextInlineElement =
2384 SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into())).into();
2385 assert_eq!(
2386 serde_json::to_value(&link)?,
2387 serde_json::json!({"type": "link", "url": "https://example.com"})
2388 );
2389
2390 let user: SlackRichTextInlineElement =
2391 SlackRichTextUser::new(SlackUserId("U1".into())).into();
2392 assert_eq!(
2393 serde_json::to_value(&user)?,
2394 serde_json::json!({"type": "user", "user_id": "U1"})
2395 );
2396
2397 let channel: SlackRichTextInlineElement =
2398 SlackRichTextChannel::new(SlackChannelId("C1".into())).into();
2399 assert_eq!(
2400 serde_json::to_value(&channel)?,
2401 serde_json::json!({"type": "channel", "channel_id": "C1"})
2402 );
2403
2404 let usergroup: SlackRichTextInlineElement =
2405 SlackRichTextUserGroup::new(SlackUserGroupId("G1".into())).into();
2406 assert_eq!(
2407 serde_json::to_value(&usergroup)?,
2408 serde_json::json!({"type": "usergroup", "usergroup_id": "G1"})
2409 );
2410
2411 let emoji: SlackRichTextInlineElement =
2412 SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into();
2413 assert_eq!(
2414 serde_json::to_value(&emoji)?,
2415 serde_json::json!({"type": "emoji", "name": "wave"})
2416 );
2417
2418 let date: SlackRichTextInlineElement = SlackRichTextDate::new(
2419 SlackDateTime("2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>()?),
2420 "{date_short}".to_string(),
2421 )
2422 .into();
2423 assert_eq!(
2424 serde_json::to_value(&date)?,
2425 serde_json::json!({"type": "date", "timestamp": 1_577_839_362_i64, "format": "{date_short}"})
2426 );
2427
2428 let broadcast: SlackRichTextInlineElement =
2429 SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into();
2430 assert_eq!(
2431 serde_json::to_value(&broadcast)?,
2432 serde_json::json!({"type": "broadcast", "range": "here"})
2433 );
2434
2435 let color: SlackRichTextInlineElement =
2436 SlackRichTextColor::new("#ff0000".to_string()).into();
2437 assert_eq!(
2438 serde_json::to_value(&color)?,
2439 serde_json::json!({"type": "color", "value": "#ff0000"})
2440 );
2441
2442 let mention: SlackRichTextInlineElement = SlackRichTextMessageMention::new(
2443 SlackRelaxedUrl("https://example.com/archives/C1/p1".into()),
2444 )
2445 .into();
2446 assert_eq!(
2447 serde_json::to_value(&mention)?,
2448 serde_json::json!({"type": "message_mention", "url": "https://example.com/archives/C1/p1"})
2449 );
2450
2451 Ok(())
2452 }
2453
2454 #[test]
2455 fn rich_text_list_accepts_str_items() -> Result<(), Box<dyn std::error::Error>> {
2456 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2457 let block: SlackBlock = serde_json::from_str(payload)?;
2458 let expected_list = match block {
2459 SlackBlock::RichText(r) => match &r.elements[1] {
2460 SlackRichTextElement::List(l) => l.clone(),
2461 other => panic!("Expected List element, got {other:?}"),
2462 },
2463 _ => panic!("Expected RichText block"),
2464 };
2465
2466 let list = SlackRichTextList::new(
2467 SlackRichTextListStyle::Bullet,
2468 vec!["Item one".into(), "Item two".into()],
2469 )
2470 .with_indent(0);
2471
2472 assert_eq!(list, expected_list);
2473 Ok(())
2474 }
2475
2476 #[test]
2477 fn rich_text_style_helpers_merge_flags() -> Result<(), Box<dyn std::error::Error>> {
2478 let bold = SlackRichTextText::new("hi".to_string()).bold();
2479 let json = serde_json::to_value(&bold)?;
2480 assert_eq!(json["style"], serde_json::json!({"bold": true}));
2481
2482 let both = SlackRichTextText::new("hi".to_string())
2483 .with_style(SlackRichTextStyle::new().with_italic(true))
2484 .bold();
2485 let json2 = serde_json::to_value(&both)?;
2486 assert_eq!(
2487 json2["style"],
2488 serde_json::json!({"bold": true, "italic": true})
2489 );
2490
2491 let struck = SlackRichTextText::new("hi".to_string()).strike();
2492 assert_eq!(
2493 serde_json::to_value(&struck)?["style"],
2494 serde_json::json!({"strike": true})
2495 );
2496
2497 let coded = SlackRichTextText::new("hi".to_string()).code();
2498 assert_eq!(
2499 serde_json::to_value(&coded)?["style"],
2500 serde_json::json!({"code": true})
2501 );
2502
2503 Ok(())
2504 }
2505
2506 #[test]
2507 fn table_block_builds_from_str_cells() -> Result<(), Box<dyn std::error::Error>> {
2508 let payload = include_str!("./fixtures/slack_table_block.json");
2509 let expected: serde_json::Value = serde_json::from_str(payload)?;
2510
2511 let block: SlackBlock = SlackTableBlock::new(vec![
2512 vec!["Header A".into(), "Header B".into()],
2513 vec![
2514 "Data 1A".into(),
2515 SlackTableRichTextCell::new(vec![SlackRichTextSection::new(vec![
2516 SlackRichTextLink::new(SlackRelaxedUrl("https://slack.com".into()))
2517 .with_text("Data 1B".to_string())
2518 .into(),
2519 ])
2520 .into()])
2521 .into(),
2522 ],
2523 ])
2524 .with_block_id(SlackBlockId("table_block_1".into()))
2525 .with_column_settings(vec![
2526 SlackTableColumnSetting::new().with_is_wrapped(true),
2527 SlackTableColumnSetting::new().with_align(SlackTableColumnAlign::Right),
2528 ])
2529 .into();
2530
2531 assert_eq!(serde_json::to_value(&block)?, expected);
2532 Ok(())
2533 }
2534
2535 #[test]
2536 fn rich_text_block_builds_fixture_with_conversions() -> Result<(), Box<dyn std::error::Error>> {
2537 let payload = include_str!("./fixtures/slack_rich_text_block.json");
2538 let expected: serde_json::Value = serde_json::from_str(payload)?;
2539
2540 let section: SlackRichTextElement = SlackRichTextSection::new(vec![
2541 SlackRichTextText::new("Hello ".to_string()).bold().into(),
2542 SlackRichTextUser::new(SlackUserId("U123ABC456".into())).into(),
2543 "! Check out ".into(),
2544 SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into()))
2545 .with_text("this link".to_string())
2546 .with_style(SlackRichTextStyle::new().with_italic(true))
2547 .into(),
2548 SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into(),
2549 SlackRichTextChannel::new(SlackChannelId("C123ABC456".into())).into(),
2550 SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into(),
2551 ])
2552 .into();
2553
2554 let list: SlackRichTextElement = SlackRichTextList::new(
2555 SlackRichTextListStyle::Bullet,
2556 vec!["Item one".into(), "Item two".into()],
2557 )
2558 .with_indent(0)
2559 .into();
2560
2561 let preformatted: SlackRichTextElement =
2562 SlackRichTextPreformatted::new(vec![SlackRichTextText::new(
2563 "fn main() {}\n".to_string(),
2564 )
2565 .into()])
2566 .with_border(1)
2567 .into();
2568
2569 let quote: SlackRichTextElement =
2570 SlackRichTextQuote::new(vec![SlackRichTextText::new("A wise quote".to_string())
2571 .italic()
2572 .into()])
2573 .into();
2574
2575 let block: SlackBlock = SlackRichTextBlock::new(vec![section, list, preformatted, quote])
2576 .with_block_id(SlackBlockId("test_block".into()))
2577 .into();
2578
2579 assert_eq!(serde_json::to_value(&block)?, expected);
2580 Ok(())
2581 }
2582}