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 url: Option<Url>,
389}
390
391#[skip_serializing_none]
392#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
393pub struct SlackBlockOptionGroup<T: Into<SlackBlockText>> {
394 pub label: SlackBlockPlainTextOnly,
395 pub options: Vec<SlackBlockChoiceItem<T>>,
396}
397
398#[skip_serializing_none]
399#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
400pub struct SlackBlockStaticSelectElement {
401 pub action_id: SlackActionId,
402 pub placeholder: Option<SlackBlockPlainTextOnly>,
403 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
404 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
405 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
406 pub confirm: Option<SlackBlockConfirmItem>,
407 pub focus_on_load: Option<bool>,
408}
409
410impl From<SlackBlockStaticSelectElement> for SlackSectionBlockElement {
411 fn from(element: SlackBlockStaticSelectElement) -> Self {
412 SlackSectionBlockElement::StaticSelect(element)
413 }
414}
415
416impl From<SlackBlockStaticSelectElement> for SlackInputBlockElement {
417 fn from(element: SlackBlockStaticSelectElement) -> Self {
418 SlackInputBlockElement::StaticSelect(element)
419 }
420}
421
422impl From<SlackBlockStaticSelectElement> for SlackActionBlockElement {
423 fn from(element: SlackBlockStaticSelectElement) -> Self {
424 SlackActionBlockElement::StaticSelect(element)
425 }
426}
427
428#[skip_serializing_none]
429#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
430pub struct SlackBlockMultiStaticSelectElement {
431 pub action_id: SlackActionId,
432 pub placeholder: Option<SlackBlockPlainTextOnly>,
433 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
434 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
435 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
436 pub confirm: Option<SlackBlockConfirmItem>,
437 pub max_selected_items: Option<u64>,
438 pub focus_on_load: Option<bool>,
439}
440
441impl From<SlackBlockMultiStaticSelectElement> for SlackSectionBlockElement {
442 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
443 SlackSectionBlockElement::MultiStaticSelect(element)
444 }
445}
446
447impl From<SlackBlockMultiStaticSelectElement> for SlackInputBlockElement {
448 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
449 SlackInputBlockElement::MultiStaticSelect(element)
450 }
451}
452
453#[skip_serializing_none]
454#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
455pub struct SlackBlockExternalSelectElement {
456 pub action_id: SlackActionId,
457 pub placeholder: Option<SlackBlockPlainTextOnly>,
458 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
459 pub confirm: Option<SlackBlockConfirmItem>,
460 pub focus_on_load: Option<bool>,
461 pub min_query_length: Option<u64>,
462}
463
464impl From<SlackBlockExternalSelectElement> for SlackSectionBlockElement {
465 fn from(element: SlackBlockExternalSelectElement) -> Self {
466 SlackSectionBlockElement::ExternalSelect(element)
467 }
468}
469
470impl From<SlackBlockExternalSelectElement> for SlackInputBlockElement {
471 fn from(element: SlackBlockExternalSelectElement) -> Self {
472 SlackInputBlockElement::ExternalSelect(element)
473 }
474}
475
476impl From<SlackBlockExternalSelectElement> for SlackActionBlockElement {
477 fn from(element: SlackBlockExternalSelectElement) -> Self {
478 SlackActionBlockElement::ExternalSelect(element)
479 }
480}
481
482#[skip_serializing_none]
483#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
484pub struct SlackBlockMultiExternalSelectElement {
485 pub action_id: SlackActionId,
486 pub placeholder: Option<SlackBlockPlainTextOnly>,
487 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
488 pub confirm: Option<SlackBlockConfirmItem>,
489 pub max_selected_items: Option<u64>,
490 pub focus_on_load: Option<bool>,
491 pub min_query_length: Option<u64>,
492}
493
494impl From<SlackBlockMultiExternalSelectElement> for SlackSectionBlockElement {
495 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
496 SlackSectionBlockElement::MultiExternalSelect(element)
497 }
498}
499
500impl From<SlackBlockMultiExternalSelectElement> for SlackInputBlockElement {
501 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
502 SlackInputBlockElement::MultiExternalSelect(element)
503 }
504}
505
506#[skip_serializing_none]
507#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
508pub struct SlackBlockUsersSelectElement {
509 pub action_id: SlackActionId,
510 pub placeholder: Option<SlackBlockPlainTextOnly>,
511 pub initial_user: Option<String>,
512 pub confirm: Option<SlackBlockConfirmItem>,
513 pub focus_on_load: Option<bool>,
514}
515
516impl From<SlackBlockUsersSelectElement> for SlackSectionBlockElement {
517 fn from(element: SlackBlockUsersSelectElement) -> Self {
518 SlackSectionBlockElement::UsersSelect(element)
519 }
520}
521
522impl From<SlackBlockUsersSelectElement> for SlackInputBlockElement {
523 fn from(element: SlackBlockUsersSelectElement) -> Self {
524 SlackInputBlockElement::UsersSelect(element)
525 }
526}
527
528impl From<SlackBlockUsersSelectElement> for SlackActionBlockElement {
529 fn from(element: SlackBlockUsersSelectElement) -> Self {
530 SlackActionBlockElement::UsersSelect(element)
531 }
532}
533
534#[skip_serializing_none]
535#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
536pub struct SlackBlockMultiUsersSelectElement {
537 pub action_id: SlackActionId,
538 pub placeholder: Option<SlackBlockPlainTextOnly>,
539 pub initial_users: Option<Vec<String>>,
540 pub confirm: Option<SlackBlockConfirmItem>,
541 pub max_selected_items: Option<u64>,
542 pub focus_on_load: Option<bool>,
543}
544
545impl From<SlackBlockMultiUsersSelectElement> for SlackSectionBlockElement {
546 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
547 SlackSectionBlockElement::MultiUsersSelect(element)
548 }
549}
550
551impl From<SlackBlockMultiUsersSelectElement> for SlackInputBlockElement {
552 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
553 SlackInputBlockElement::MultiUsersSelect(element)
554 }
555}
556
557#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
558pub enum SlackConversationFilterInclude {
559 #[serde(rename = "im")]
560 Im,
561 #[serde(rename = "mpim")]
562 Mpim,
563 #[serde(rename = "public")]
564 Public,
565 #[serde(rename = "private")]
566 Private,
567}
568
569#[skip_serializing_none]
570#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
571pub struct SlackBlockConversationFilter {
572 pub include: Option<Vec<SlackConversationFilterInclude>>,
573 pub exclude_external_shared_channels: Option<bool>,
574 pub exclude_bot_users: Option<bool>,
575}
576
577#[skip_serializing_none]
578#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
579pub struct SlackBlockConversationsSelectElement {
580 pub action_id: SlackActionId,
581 pub placeholder: Option<SlackBlockPlainTextOnly>,
582 pub initial_conversation: Option<SlackConversationId>,
583 pub default_to_current_conversation: Option<bool>,
584 pub confirm: Option<SlackBlockConfirmItem>,
585 pub response_url_enabled: Option<bool>,
586 pub focus_on_load: Option<bool>,
587 pub filter: Option<SlackBlockConversationFilter>,
588}
589
590impl From<SlackBlockConversationsSelectElement> for SlackSectionBlockElement {
591 fn from(element: SlackBlockConversationsSelectElement) -> Self {
592 SlackSectionBlockElement::ConversationsSelect(element)
593 }
594}
595
596impl From<SlackBlockConversationsSelectElement> for SlackInputBlockElement {
597 fn from(element: SlackBlockConversationsSelectElement) -> Self {
598 SlackInputBlockElement::ConversationsSelect(element)
599 }
600}
601
602impl From<SlackBlockConversationsSelectElement> for SlackActionBlockElement {
603 fn from(element: SlackBlockConversationsSelectElement) -> Self {
604 SlackActionBlockElement::ConversationsSelect(element)
605 }
606}
607
608#[skip_serializing_none]
609#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
610pub struct SlackBlockMultiConversationsSelectElement {
611 pub action_id: SlackActionId,
612 pub placeholder: Option<SlackBlockPlainTextOnly>,
613 pub initial_conversations: Option<Vec<SlackConversationId>>,
614 pub default_to_current_conversation: Option<bool>,
615 pub confirm: Option<SlackBlockConfirmItem>,
616 pub max_selected_items: Option<u64>,
617 pub focus_on_load: Option<bool>,
618 pub filter: Option<SlackBlockConversationFilter>,
619}
620
621impl From<SlackBlockMultiConversationsSelectElement> for SlackSectionBlockElement {
622 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
623 SlackSectionBlockElement::MultiConversationsSelect(element)
624 }
625}
626
627impl From<SlackBlockMultiConversationsSelectElement> for SlackInputBlockElement {
628 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
629 SlackInputBlockElement::MultiConversationsSelect(element)
630 }
631}
632
633#[skip_serializing_none]
634#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
635pub struct SlackBlockChannelsSelectElement {
636 pub action_id: SlackActionId,
637 pub placeholder: Option<SlackBlockPlainTextOnly>,
638 pub initial_channel: Option<SlackChannelId>,
639 pub confirm: Option<SlackBlockConfirmItem>,
640 pub response_url_enabled: Option<bool>,
641 pub focus_on_load: Option<bool>,
642}
643
644impl From<SlackBlockChannelsSelectElement> for SlackSectionBlockElement {
645 fn from(element: SlackBlockChannelsSelectElement) -> Self {
646 SlackSectionBlockElement::ChannelsSelect(element)
647 }
648}
649
650impl From<SlackBlockChannelsSelectElement> for SlackInputBlockElement {
651 fn from(element: SlackBlockChannelsSelectElement) -> Self {
652 SlackInputBlockElement::ChannelsSelect(element)
653 }
654}
655
656impl From<SlackBlockChannelsSelectElement> for SlackActionBlockElement {
657 fn from(element: SlackBlockChannelsSelectElement) -> Self {
658 SlackActionBlockElement::ChannelsSelect(element)
659 }
660}
661
662#[skip_serializing_none]
663#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
664pub struct SlackBlockMultiChannelsSelectElement {
665 pub action_id: SlackActionId,
666 pub placeholder: Option<SlackBlockPlainTextOnly>,
667 pub initial_channels: Option<Vec<SlackChannelId>>,
668 pub confirm: Option<SlackBlockConfirmItem>,
669 pub max_selected_items: Option<u64>,
670 pub focus_on_load: Option<bool>,
671}
672
673impl From<SlackBlockMultiChannelsSelectElement> for SlackSectionBlockElement {
674 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
675 SlackSectionBlockElement::MultiChannelsSelect(element)
676 }
677}
678
679impl From<SlackBlockMultiChannelsSelectElement> for SlackInputBlockElement {
680 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
681 SlackInputBlockElement::MultiChannelsSelect(element)
682 }
683}
684
685#[skip_serializing_none]
686#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
687pub struct SlackBlockOverflowElement {
688 pub action_id: SlackActionId,
689 pub options: Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
690 pub confirm: Option<SlackBlockConfirmItem>,
691}
692
693impl From<SlackBlockOverflowElement> for SlackSectionBlockElement {
694 fn from(element: SlackBlockOverflowElement) -> Self {
695 SlackSectionBlockElement::Overflow(element)
696 }
697}
698
699impl From<SlackBlockOverflowElement> for SlackActionBlockElement {
700 fn from(element: SlackBlockOverflowElement) -> Self {
701 SlackActionBlockElement::Overflow(element)
702 }
703}
704
705#[skip_serializing_none]
706#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
707pub struct SlackBlockDatePickerElement {
708 pub action_id: SlackActionId,
709 pub placeholder: Option<SlackBlockPlainTextOnly>,
710 pub initial_date: Option<String>,
711 pub confirm: Option<SlackBlockConfirmItem>,
712 pub focus_on_load: Option<bool>,
713}
714
715impl From<SlackBlockDatePickerElement> for SlackSectionBlockElement {
716 fn from(element: SlackBlockDatePickerElement) -> Self {
717 SlackSectionBlockElement::DatePicker(element)
718 }
719}
720
721impl From<SlackBlockDatePickerElement> for SlackInputBlockElement {
722 fn from(element: SlackBlockDatePickerElement) -> Self {
723 SlackInputBlockElement::DatePicker(element)
724 }
725}
726
727impl From<SlackBlockDatePickerElement> for SlackActionBlockElement {
728 fn from(element: SlackBlockDatePickerElement) -> Self {
729 SlackActionBlockElement::DatePicker(element)
730 }
731}
732
733#[skip_serializing_none]
734#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
735pub struct SlackBlockTimePickerElement {
736 pub action_id: SlackActionId,
737 pub initial_time: Option<String>,
738 pub confirm: Option<SlackBlockConfirmItem>,
739 pub focus_on_load: Option<bool>,
740 pub placeholder: Option<SlackBlockPlainTextOnly>,
741 pub timezone: Option<String>,
742}
743
744impl From<SlackBlockTimePickerElement> for SlackSectionBlockElement {
745 fn from(element: SlackBlockTimePickerElement) -> Self {
746 SlackSectionBlockElement::TimePicker(element)
747 }
748}
749
750impl From<SlackBlockTimePickerElement> for SlackInputBlockElement {
751 fn from(element: SlackBlockTimePickerElement) -> Self {
752 SlackInputBlockElement::TimePicker(element)
753 }
754}
755
756impl From<SlackBlockTimePickerElement> for SlackActionBlockElement {
757 fn from(element: SlackBlockTimePickerElement) -> Self {
758 SlackActionBlockElement::TimePicker(element)
759 }
760}
761
762#[skip_serializing_none]
763#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
764pub struct SlackBlockDateTimePickerElement {
765 pub action_id: SlackActionId,
766 pub initial_date_time: Option<SlackDateTime>,
767 pub confirm: Option<SlackBlockConfirmItem>,
768 pub focus_on_load: Option<bool>,
769}
770
771impl From<SlackBlockDateTimePickerElement> for SlackInputBlockElement {
772 fn from(element: SlackBlockDateTimePickerElement) -> Self {
773 SlackInputBlockElement::DateTimePicker(element)
774 }
775}
776
777impl From<SlackBlockDateTimePickerElement> for SlackActionBlockElement {
778 fn from(element: SlackBlockDateTimePickerElement) -> Self {
779 SlackActionBlockElement::DateTimePicker(element)
780 }
781}
782
783#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
787#[serde(rename_all = "snake_case")]
788pub enum SlackDispatchActionTrigger {
789 OnEnterPressed,
790 OnCharacterEntered,
791}
792
793#[skip_serializing_none]
794#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
795pub struct SlackDispatchActionConfig {
796 pub trigger_actions_on: Option<Vec<SlackDispatchActionTrigger>>,
797}
798
799#[skip_serializing_none]
800#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
801pub struct SlackBlockPlainTextInputElement {
802 pub action_id: SlackActionId,
803 pub placeholder: Option<SlackBlockPlainTextOnly>,
804 pub initial_value: Option<String>,
805 pub multiline: Option<bool>,
806 pub min_length: Option<u64>,
807 pub max_length: Option<u64>,
808 pub focus_on_load: Option<bool>,
809 pub dispatch_action_config: Option<SlackDispatchActionConfig>,
810}
811
812impl From<SlackBlockPlainTextInputElement> for SlackSectionBlockElement {
813 fn from(element: SlackBlockPlainTextInputElement) -> Self {
814 SlackSectionBlockElement::PlainTextInput(element)
815 }
816}
817
818impl From<SlackBlockPlainTextInputElement> for SlackInputBlockElement {
819 fn from(element: SlackBlockPlainTextInputElement) -> Self {
820 SlackInputBlockElement::PlainTextInput(element)
821 }
822}
823
824impl From<SlackBlockPlainTextInputElement> for SlackActionBlockElement {
825 fn from(element: SlackBlockPlainTextInputElement) -> Self {
826 SlackActionBlockElement::PlainTextInput(element)
827 }
828}
829
830#[skip_serializing_none]
831#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
832pub struct SlackBlockNumberInputElement {
833 pub action_id: SlackActionId,
834 pub is_decimal_allowed: bool,
835 pub focus_on_load: Option<bool>,
836 pub placeholder: Option<SlackBlockPlainTextOnly>,
837 pub initial_value: Option<String>,
838 pub min_value: Option<String>,
839 pub max_value: Option<String>,
840}
841
842impl From<SlackBlockNumberInputElement> for SlackSectionBlockElement {
843 fn from(element: SlackBlockNumberInputElement) -> Self {
844 SlackSectionBlockElement::NumberInput(element)
845 }
846}
847
848impl From<SlackBlockNumberInputElement> for SlackInputBlockElement {
849 fn from(element: SlackBlockNumberInputElement) -> Self {
850 SlackInputBlockElement::NumberInput(element)
851 }
852}
853
854impl From<SlackBlockNumberInputElement> for SlackActionBlockElement {
855 fn from(element: SlackBlockNumberInputElement) -> Self {
856 SlackActionBlockElement::NumberInput(element)
857 }
858}
859
860#[skip_serializing_none]
861#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
862pub struct SlackBlockUrlInputElement {
863 pub action_id: SlackActionId,
864 pub placeholder: Option<SlackBlockPlainTextOnly>,
865 pub initial_value: Option<String>,
866}
867
868impl From<SlackBlockUrlInputElement> for SlackSectionBlockElement {
869 fn from(element: SlackBlockUrlInputElement) -> Self {
870 SlackSectionBlockElement::UrlInput(element)
871 }
872}
873
874impl From<SlackBlockUrlInputElement> for SlackInputBlockElement {
875 fn from(element: SlackBlockUrlInputElement) -> Self {
876 SlackInputBlockElement::UrlInput(element)
877 }
878}
879
880impl From<SlackBlockUrlInputElement> for SlackActionBlockElement {
881 fn from(element: SlackBlockUrlInputElement) -> Self {
882 SlackActionBlockElement::UrlInput(element)
883 }
884}
885
886#[skip_serializing_none]
887#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
888pub struct SlackBlockEmailInputElement {
889 pub action_id: SlackActionId,
890 pub focus_on_load: Option<bool>,
891 pub placeholder: Option<SlackBlockPlainTextOnly>,
892 pub initial_value: Option<EmailAddress>,
893}
894
895impl From<SlackBlockEmailInputElement> for SlackInputBlockElement {
896 fn from(element: SlackBlockEmailInputElement) -> Self {
897 SlackInputBlockElement::EmailInput(element)
898 }
899}
900
901#[skip_serializing_none]
902#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
903pub struct SlackBlockRadioButtonsElement {
904 pub action_id: SlackActionId,
905 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
906 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockText>>,
907 pub confirm: Option<SlackBlockConfirmItem>,
908 pub focus_on_load: Option<bool>,
909}
910
911impl From<SlackBlockRadioButtonsElement> for SlackSectionBlockElement {
912 fn from(element: SlackBlockRadioButtonsElement) -> Self {
913 SlackSectionBlockElement::RadioButtons(element)
914 }
915}
916
917impl From<SlackBlockRadioButtonsElement> for SlackInputBlockElement {
918 fn from(element: SlackBlockRadioButtonsElement) -> Self {
919 SlackInputBlockElement::RadioButtons(element)
920 }
921}
922
923impl From<SlackBlockRadioButtonsElement> for SlackActionBlockElement {
924 fn from(element: SlackBlockRadioButtonsElement) -> Self {
925 SlackActionBlockElement::RadioButtons(element)
926 }
927}
928
929#[skip_serializing_none]
930#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
931pub struct SlackBlockCheckboxesElement {
932 pub action_id: SlackActionId,
933 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
934 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockText>>>,
935 pub confirm: Option<SlackBlockConfirmItem>,
936 pub focus_on_load: Option<bool>,
937}
938
939impl From<SlackBlockCheckboxesElement> for SlackSectionBlockElement {
940 fn from(element: SlackBlockCheckboxesElement) -> Self {
941 SlackSectionBlockElement::Checkboxes(element)
942 }
943}
944
945impl From<SlackBlockCheckboxesElement> for SlackInputBlockElement {
946 fn from(element: SlackBlockCheckboxesElement) -> Self {
947 SlackInputBlockElement::Checkboxes(element)
948 }
949}
950
951impl From<SlackBlockCheckboxesElement> for SlackActionBlockElement {
952 fn from(element: SlackBlockCheckboxesElement) -> Self {
953 SlackActionBlockElement::Checkboxes(element)
954 }
955}
956
957#[skip_serializing_none]
961#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
962pub struct SlackBlockPlainText {
963 pub text: String,
964 pub emoji: Option<bool>,
965}
966
967#[skip_serializing_none]
971#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
972pub struct SlackBlockMarkDownText {
973 pub text: String,
974 pub verbatim: Option<bool>,
975}
976
977#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
981#[serde(tag = "type")]
982pub enum SlackBlockText {
983 #[serde(rename = "plain_text")]
984 Plain(SlackBlockPlainText),
985 #[serde(rename = "mrkdwn")]
986 MarkDown(SlackBlockMarkDownText),
987}
988
989#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
990#[serde(tag = "type", rename = "plain_text")]
991pub struct SlackBlockPlainTextOnly {
992 #[serde(flatten)]
993 value: SlackBlockPlainText,
994}
995
996impl SlackBlockPlainText {
997 pub fn as_block_text(&self) -> SlackBlockText {
998 SlackBlockText::Plain(self.clone())
999 }
1000}
1001
1002impl From<String> for SlackBlockPlainText {
1003 fn from(value: String) -> Self {
1004 SlackBlockPlainText::new(value)
1005 }
1006}
1007
1008impl From<&str> for SlackBlockPlainText {
1009 fn from(value: &str) -> Self {
1010 SlackBlockPlainText::new(String::from(value))
1011 }
1012}
1013
1014impl SlackBlockMarkDownText {
1015 pub fn as_block_text(&self) -> SlackBlockText {
1016 SlackBlockText::MarkDown(self.clone())
1017 }
1018}
1019
1020impl From<String> for SlackBlockMarkDownText {
1021 fn from(value: String) -> Self {
1022 SlackBlockMarkDownText::new(value)
1023 }
1024}
1025
1026impl From<&str> for SlackBlockMarkDownText {
1027 fn from(value: &str) -> Self {
1028 SlackBlockMarkDownText::new(String::from(value))
1029 }
1030}
1031
1032impl From<SlackBlockPlainText> for SlackBlockPlainTextOnly {
1033 fn from(pt: SlackBlockPlainText) -> Self {
1034 SlackBlockPlainTextOnly { value: pt }
1035 }
1036}
1037
1038impl From<SlackBlockPlainText> for SlackBlockText {
1039 fn from(text: SlackBlockPlainText) -> Self {
1040 SlackBlockText::Plain(text)
1041 }
1042}
1043
1044impl From<SlackBlockMarkDownText> for SlackBlockText {
1045 fn from(text: SlackBlockMarkDownText) -> Self {
1046 SlackBlockText::MarkDown(text)
1047 }
1048}
1049
1050impl From<SlackBlockPlainText> for SlackContextBlockElement {
1051 fn from(text: SlackBlockPlainText) -> Self {
1052 SlackContextBlockElement::Plain(text)
1053 }
1054}
1055
1056impl From<SlackBlockMarkDownText> for SlackContextBlockElement {
1057 fn from(text: SlackBlockMarkDownText) -> Self {
1058 SlackContextBlockElement::MarkDown(text)
1059 }
1060}
1061
1062impl From<SlackBlockPlainTextOnly> for SlackBlockText {
1063 fn from(text: SlackBlockPlainTextOnly) -> Self {
1064 SlackBlockText::Plain(text.value)
1065 }
1066}
1067
1068impl From<String> for SlackBlockPlainTextOnly {
1069 fn from(value: String) -> Self {
1070 SlackBlockPlainTextOnly {
1071 value: value.into(),
1072 }
1073 }
1074}
1075
1076impl From<&str> for SlackBlockPlainTextOnly {
1077 fn from(value: &str) -> Self {
1078 SlackBlockPlainTextOnly {
1079 value: value.into(),
1080 }
1081 }
1082}
1083
1084#[skip_serializing_none]
1088#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1089pub struct SlackVideoBlock {
1090 pub alt_text: String,
1091 pub author_name: Option<String>,
1092 pub block_id: Option<SlackBlockId>,
1093 pub description: Option<SlackBlockPlainTextOnly>,
1094 pub provider_icon_url: Option<Url>,
1095 pub provider_name: Option<String>,
1096 pub title: SlackBlockPlainTextOnly,
1097 pub title_url: Option<Url>,
1098 pub thumbnail_url: Url,
1099 pub video_url: Url,
1100}
1101
1102impl From<SlackVideoBlock> for SlackBlock {
1103 fn from(block: SlackVideoBlock) -> Self {
1104 SlackBlock::Video(block)
1105 }
1106}
1107
1108#[skip_serializing_none]
1112#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1113pub struct SlackMarkdownBlock {
1114 pub block_id: Option<SlackBlockId>,
1115 pub text: String,
1116}
1117
1118impl From<SlackMarkdownBlock> for SlackBlock {
1119 fn from(block: SlackMarkdownBlock) -> Self {
1120 SlackBlock::Markdown(block)
1121 }
1122}
1123
1124#[skip_serializing_none]
1128#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1129pub struct SlackRichTextBlock {
1130 pub block_id: Option<SlackBlockId>,
1131 pub elements: Vec<SlackRichTextElement>,
1132}
1133
1134impl From<SlackRichTextBlock> for SlackBlock {
1135 fn from(block: SlackRichTextBlock) -> Self {
1136 SlackBlock::RichText(block)
1137 }
1138}
1139
1140#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1141#[serde(tag = "type", rename_all = "snake_case")]
1142pub enum SlackRichTextInlineContent {
1143 #[serde(rename = "rich_text")]
1144 RichText(SlackRichTextBlock),
1145}
1146
1147impl From<SlackRichTextBlock> for SlackRichTextInlineContent {
1148 fn from(block: SlackRichTextBlock) -> Self {
1149 SlackRichTextInlineContent::RichText(block)
1150 }
1151}
1152
1153#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1154#[serde(tag = "type")]
1155pub enum SlackRichTextElement {
1156 #[serde(rename = "rich_text_section")]
1157 Section(SlackRichTextSection),
1158 #[serde(rename = "rich_text_list")]
1159 List(SlackRichTextList),
1160 #[serde(rename = "rich_text_preformatted")]
1161 Preformatted(SlackRichTextPreformatted),
1162 #[serde(rename = "rich_text_quote")]
1163 Quote(SlackRichTextQuote),
1164}
1165
1166impl From<SlackRichTextSection> for SlackRichTextElement {
1167 fn from(element: SlackRichTextSection) -> Self {
1168 SlackRichTextElement::Section(element)
1169 }
1170}
1171
1172impl From<SlackRichTextList> for SlackRichTextElement {
1173 fn from(list: SlackRichTextList) -> Self {
1174 SlackRichTextElement::List(list)
1175 }
1176}
1177
1178impl From<SlackRichTextPreformatted> for SlackRichTextElement {
1179 fn from(element: SlackRichTextPreformatted) -> Self {
1180 SlackRichTextElement::Preformatted(element)
1181 }
1182}
1183
1184impl From<SlackRichTextQuote> for SlackRichTextElement {
1185 fn from(element: SlackRichTextQuote) -> Self {
1186 SlackRichTextElement::Quote(element)
1187 }
1188}
1189
1190#[skip_serializing_none]
1191#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1192pub struct SlackRichTextSection {
1193 pub elements: Vec<SlackRichTextInlineElement>,
1194}
1195
1196#[skip_serializing_none]
1197#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1198pub struct SlackRichTextList {
1199 pub style: SlackRichTextListStyle,
1200 pub elements: Vec<SlackRichTextListElement>,
1201 pub indent: Option<u64>,
1202 pub offset: Option<u64>,
1203 pub border: Option<u64>,
1204}
1205
1206#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1207#[serde(tag = "type")]
1208pub enum SlackRichTextListElement {
1209 #[serde(rename = "rich_text_section")]
1210 Section(SlackRichTextSection),
1211}
1212
1213impl From<SlackRichTextSection> for SlackRichTextListElement {
1214 fn from(element: SlackRichTextSection) -> Self {
1215 SlackRichTextListElement::Section(element)
1216 }
1217}
1218
1219#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1220#[serde(rename_all = "snake_case")]
1221pub enum SlackRichTextListStyle {
1222 Bullet,
1223 Ordered,
1224}
1225
1226#[skip_serializing_none]
1227#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1228pub struct SlackRichTextPreformatted {
1229 pub elements: Vec<SlackRichTextInlineElement>,
1230 pub border: Option<u64>,
1231 pub language: Option<String>,
1232}
1233
1234#[skip_serializing_none]
1235#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1236pub struct SlackRichTextQuote {
1237 pub elements: Vec<SlackRichTextInlineElement>,
1238 pub border: Option<u64>,
1239}
1240
1241#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1242#[serde(tag = "type")]
1243pub enum SlackRichTextInlineElement {
1244 #[serde(rename = "text")]
1245 Text(SlackRichTextText),
1246 #[serde(rename = "link")]
1247 Link(SlackRichTextLink),
1248 #[serde(rename = "user")]
1249 User(SlackRichTextUser),
1250 #[serde(rename = "channel")]
1251 Channel(SlackRichTextChannel),
1252 #[serde(rename = "usergroup")]
1253 UserGroup(SlackRichTextUserGroup),
1254 #[serde(rename = "emoji")]
1255 Emoji(SlackRichTextEmoji),
1256 #[serde(rename = "date")]
1257 Date(SlackRichTextDate),
1258 #[serde(rename = "broadcast")]
1259 Broadcast(SlackRichTextBroadcast),
1260 #[serde(rename = "color")]
1261 Color(SlackRichTextColor),
1262}
1263
1264#[skip_serializing_none]
1265#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1266pub struct SlackRichTextStyle {
1267 pub bold: Option<bool>,
1268 pub italic: Option<bool>,
1269 pub strike: Option<bool>,
1270 pub code: Option<bool>,
1271 pub underline: Option<bool>,
1272 pub highlight: Option<bool>,
1273 pub client_highlight: Option<bool>,
1274 pub unlink: Option<bool>,
1275}
1276
1277#[skip_serializing_none]
1278#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1279pub struct SlackRichTextText {
1280 pub text: String,
1281 pub style: Option<SlackRichTextStyle>,
1282}
1283
1284#[skip_serializing_none]
1285#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1286pub struct SlackRichTextLink {
1287 pub url: SlackRelaxedUrl,
1288 pub text: Option<String>,
1289 #[serde(rename = "unsafe")]
1290 pub unsafe_: Option<bool>,
1291 pub style: Option<SlackRichTextStyle>,
1292}
1293
1294#[skip_serializing_none]
1295#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1296pub struct SlackRichTextUser {
1297 pub user_id: SlackUserId,
1298 pub style: Option<SlackRichTextStyle>,
1299}
1300
1301#[skip_serializing_none]
1302#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1303pub struct SlackRichTextChannel {
1304 pub channel_id: SlackChannelId,
1305 pub style: Option<SlackRichTextStyle>,
1306}
1307
1308#[skip_serializing_none]
1309#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1310pub struct SlackRichTextUserGroup {
1311 pub usergroup_id: SlackUserGroupId,
1312 pub style: Option<SlackRichTextStyle>,
1313}
1314
1315#[skip_serializing_none]
1316#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1317pub struct SlackRichTextEmoji {
1318 pub name: SlackEmojiName,
1319 pub unicode: Option<String>,
1320}
1321
1322#[skip_serializing_none]
1323#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1324pub struct SlackRichTextDate {
1325 pub timestamp: SlackDateTime,
1326 pub format: String,
1327 pub fallback: Option<String>,
1328 pub style: Option<SlackRichTextStyle>,
1329}
1330
1331#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1332#[serde(rename_all = "snake_case")]
1333pub enum SlackRichTextBroadcastRange {
1334 Here,
1335 Channel,
1336 Everyone,
1337}
1338
1339#[skip_serializing_none]
1340#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1341pub struct SlackRichTextBroadcast {
1342 pub range: SlackRichTextBroadcastRange,
1343 pub style: Option<SlackRichTextStyle>,
1344}
1345
1346#[skip_serializing_none]
1347#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1348pub struct SlackRichTextColor {
1349 pub value: String,
1350}
1351
1352#[skip_serializing_none]
1356#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1357pub struct SlackBlockRichTextInputElement {
1358 pub action_id: SlackActionId,
1359 pub initial_value: Option<SlackRichTextBlock>,
1360 pub focus_on_load: Option<bool>,
1361 pub placeholder: Option<SlackBlockPlainTextOnly>,
1362}
1363
1364impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1365 fn from(element: SlackBlockRichTextInputElement) -> Self {
1366 SlackInputBlockElement::RichTextInput(element)
1367 }
1368}
1369
1370#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1371#[serde(untagged)]
1372pub enum SlackImageUrlOrFile {
1373 ImageUrl { image_url: Url },
1374 SlackFile { slack_file: SlackFileIdOrUrl },
1375}
1376
1377impl SlackImageUrlOrFile {
1378 pub fn image_url(&self) -> Option<&Url> {
1379 match self {
1380 SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1381 SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1382 SlackFileIdOrUrl::Url { url } => Some(url),
1383 _ => None,
1384 },
1385 }
1386 }
1387}
1388
1389impl From<Url> for SlackImageUrlOrFile {
1390 fn from(value: Url) -> Self {
1391 SlackImageUrlOrFile::ImageUrl { image_url: value }
1392 }
1393}
1394
1395#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1396#[serde(untagged)]
1397pub enum SlackFileIdOrUrl {
1398 Id { id: SlackFileId },
1399 Url { url: Url },
1400}
1401
1402impl From<SlackFileId> for SlackFileIdOrUrl {
1403 fn from(value: SlackFileId) -> Self {
1404 SlackFileIdOrUrl::Id { id: value }
1405 }
1406}
1407
1408#[skip_serializing_none]
1412#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1413pub struct SlackTableBlock {
1414 pub block_id: Option<SlackBlockId>,
1415 pub rows: Vec<Vec<SlackTableCell>>,
1416 pub column_settings: Option<Vec<SlackTableColumnSetting>>,
1417}
1418
1419impl From<SlackTableBlock> for SlackBlock {
1420 fn from(block: SlackTableBlock) -> Self {
1421 SlackBlock::Table(block)
1422 }
1423}
1424
1425#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1426#[serde(tag = "type")]
1427pub enum SlackTableCell {
1428 #[serde(rename = "raw_text")]
1429 RawText(SlackTableRawTextCell),
1430 #[serde(rename = "rich_text")]
1431 RichText(SlackTableRichTextCell),
1432}
1433
1434#[skip_serializing_none]
1435#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1436pub struct SlackTableRawTextCell {
1437 pub text: String,
1438}
1439
1440#[skip_serializing_none]
1441#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1442pub struct SlackTableRichTextCell {
1443 pub elements: Vec<SlackRichTextElement>,
1444}
1445
1446#[skip_serializing_none]
1447#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1448pub struct SlackTableColumnSetting {
1449 pub align: Option<SlackTableColumnAlign>,
1450 pub is_wrapped: Option<bool>,
1451}
1452
1453#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1454#[serde(rename_all = "snake_case")]
1455pub enum SlackTableColumnAlign {
1456 Left,
1457 Center,
1458 Right,
1459}
1460
1461#[skip_serializing_none]
1465#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1466pub struct SlackTaskCardBlock {
1467 pub task_id: SlackTaskId,
1468 pub title: String,
1469 pub block_id: Option<SlackBlockId>,
1470 pub status: Option<SlackTaskCardStatus>,
1471 #[serde(rename = "details")]
1472 pub details: Option<SlackRichTextInlineContent>,
1473 #[serde(rename = "output")]
1474 pub output: Option<SlackRichTextInlineContent>,
1475 pub sources: Option<Vec<SlackTaskCardSource>>,
1476}
1477
1478impl From<SlackTaskCardBlock> for SlackBlock {
1479 fn from(block: SlackTaskCardBlock) -> Self {
1480 SlackBlock::TaskCard(block)
1481 }
1482}
1483
1484#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1485#[serde(rename_all = "snake_case")]
1486pub enum SlackTaskCardStatus {
1487 Pending,
1488 InProgress,
1489 Complete,
1490 Error,
1491}
1492
1493#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1497pub struct SlackUrlSourceElement {
1498 pub url: Url,
1499 pub text: String,
1500}
1501
1502#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1503#[serde(tag = "type")]
1504pub enum SlackTaskCardSource {
1505 #[serde(rename = "url")]
1506 Url(SlackUrlSourceElement),
1507}
1508
1509impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1510 fn from(element: SlackUrlSourceElement) -> Self {
1511 SlackTaskCardSource::Url(element)
1512 }
1513}
1514
1515#[skip_serializing_none]
1519#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1520pub struct SlackBlockFileInputElement {
1521 pub action_id: SlackActionId,
1522 pub filetypes: Option<Vec<String>>,
1523 pub max_files: Option<u64>,
1524}
1525
1526impl From<SlackBlockFileInputElement> for SlackInputBlockElement {
1527 fn from(element: SlackBlockFileInputElement) -> Self {
1528 SlackInputBlockElement::FileInput(element)
1529 }
1530}
1531
1532#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1536#[serde(rename_all = "snake_case")]
1537pub enum SlackAlertLevel {
1538 Warning,
1539 Error,
1540 Info,
1541 Success,
1542}
1543
1544#[skip_serializing_none]
1545#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1546pub struct SlackAlertBlock {
1547 pub block_id: Option<SlackBlockId>,
1548 pub text: SlackBlockText,
1549 pub level: Option<SlackAlertLevel>,
1550}
1551
1552impl From<SlackAlertBlock> for SlackBlock {
1553 fn from(block: SlackAlertBlock) -> Self {
1554 SlackBlock::Alert(block)
1555 }
1556}
1557
1558#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1562#[serde(tag = "type")]
1563pub enum SlackCardImageElement {
1564 #[serde(rename = "image")]
1565 Image(SlackBlockImageElement),
1566}
1567
1568impl From<SlackBlockImageElement> for SlackCardImageElement {
1569 fn from(element: SlackBlockImageElement) -> Self {
1570 SlackCardImageElement::Image(element)
1571 }
1572}
1573
1574#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1575#[serde(tag = "type")]
1576pub enum SlackCardActionBlockElement {
1577 #[serde(rename = "button")]
1578 Button(SlackBlockButtonElement),
1579}
1580
1581impl From<SlackBlockButtonElement> for SlackCardActionBlockElement {
1582 fn from(element: SlackBlockButtonElement) -> Self {
1583 SlackCardActionBlockElement::Button(element)
1584 }
1585}
1586
1587#[skip_serializing_none]
1588#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1589pub struct SlackCardBlock {
1590 pub block_id: Option<SlackBlockId>,
1591 pub title: Option<SlackBlockText>,
1592 pub subtitle: Option<SlackBlockText>,
1593 pub body: Option<SlackBlockText>,
1594 pub hero_image: Option<SlackCardImageElement>,
1595 pub icon: Option<SlackCardImageElement>,
1596 pub actions: Option<Vec<SlackCardActionBlockElement>>,
1597}
1598
1599impl From<SlackCardBlock> for SlackBlock {
1600 fn from(block: SlackCardBlock) -> Self {
1601 SlackBlock::Card(block)
1602 }
1603}
1604
1605#[skip_serializing_none]
1609#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1610pub struct SlackCarouselBlock {
1611 pub block_id: Option<SlackBlockId>,
1612 pub elements: Vec<SlackBlock>,
1613}
1614
1615impl From<SlackCarouselBlock> for SlackBlock {
1616 fn from(block: SlackCarouselBlock) -> Self {
1617 SlackBlock::Carousel(block)
1618 }
1619}
1620
1621#[skip_serializing_none]
1627#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1628pub struct SlackFeedbackButtonItem {
1629 pub action_id: SlackActionId,
1630 pub value: String,
1631 pub text: SlackBlockPlainTextOnly,
1632 pub confirm: Option<SlackBlockConfirmItem>,
1633}
1634
1635#[skip_serializing_none]
1636#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1637pub struct SlackBlockFeedbackButtonsElement {
1638 pub action_id: SlackActionId,
1639 pub positive: SlackFeedbackButtonItem,
1640 pub negative: SlackFeedbackButtonItem,
1641}
1642
1643#[skip_serializing_none]
1644#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1645pub struct SlackBlockIconButtonElement {
1646 pub action_id: SlackActionId,
1647 pub icon: String,
1648 pub text: SlackBlockPlainTextOnly,
1649 pub value: Option<String>,
1650 pub confirm: Option<SlackBlockConfirmItem>,
1651 pub accessibility_label: Option<SlackAccessibilityLabel>,
1652 pub visible_to_user_ids: Option<Vec<SlackUserId>>,
1653}
1654
1655#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1656#[serde(tag = "type")]
1657pub enum SlackContextActionBlockElement {
1658 #[serde(rename = "feedback_buttons")]
1659 FeedbackButtons(SlackBlockFeedbackButtonsElement),
1660 #[serde(rename = "icon_button")]
1661 IconButton(SlackBlockIconButtonElement),
1662}
1663
1664impl From<SlackBlockFeedbackButtonsElement> for SlackContextActionBlockElement {
1665 fn from(element: SlackBlockFeedbackButtonsElement) -> Self {
1666 SlackContextActionBlockElement::FeedbackButtons(element)
1667 }
1668}
1669
1670impl From<SlackBlockIconButtonElement> for SlackContextActionBlockElement {
1671 fn from(element: SlackBlockIconButtonElement) -> Self {
1672 SlackContextActionBlockElement::IconButton(element)
1673 }
1674}
1675
1676#[skip_serializing_none]
1677#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1678pub struct SlackContextActionsBlock {
1679 pub block_id: Option<SlackBlockId>,
1680 pub elements: Vec<SlackContextActionBlockElement>,
1681}
1682
1683impl From<SlackContextActionsBlock> for SlackBlock {
1684 fn from(block: SlackContextActionsBlock) -> Self {
1685 SlackBlock::ContextActions(block)
1686 }
1687}
1688
1689#[cfg(test)]
1690mod test {
1691 use super::*;
1692 use crate::blocks::SlackHomeView;
1693
1694 #[test]
1695 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1696 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1697 let block: SlackBlock = serde_json::from_str(payload)?;
1698 match block {
1699 SlackBlock::Section(section) => match section.accessory {
1700 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1701 let filter = elem.filter.expect("filter should be present");
1702 let include = filter.include.expect("include should be present");
1703 assert_eq!(include.len(), 2);
1704 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1705 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1706 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1707 assert_eq!(filter.exclude_bot_users, Some(true));
1708 }
1709 _ => panic!("Expected ConversationsSelect accessory"),
1710 },
1711 _ => panic!("Expected Section block"),
1712 }
1713 Ok(())
1714 }
1715
1716 #[test]
1717 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1718 let filter = SlackBlockConversationFilter::new()
1719 .with_include(vec![
1720 SlackConversationFilterInclude::Im,
1721 SlackConversationFilterInclude::Mpim,
1722 ])
1723 .with_exclude_bot_users(true);
1724
1725 let json = serde_json::to_value(&filter)?;
1726 assert_eq!(
1727 json,
1728 serde_json::json!({
1729 "include": ["im", "mpim"],
1730 "exclude_bot_users": true
1731 })
1732 );
1733 Ok(())
1734 }
1735
1736 #[test]
1737 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1738 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1739 .with_filter(
1740 SlackBlockConversationFilter::new()
1741 .with_include(vec![SlackConversationFilterInclude::Public])
1742 .with_exclude_external_shared_channels(true),
1743 );
1744
1745 let json = serde_json::to_string(&elem)?;
1746 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1747 assert_eq!(elem, parsed);
1748 Ok(())
1749 }
1750
1751 #[test]
1752 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1753 let elem =
1754 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1755 .with_filter(
1756 SlackBlockConversationFilter::new()
1757 .with_include(vec![
1758 SlackConversationFilterInclude::Public,
1759 SlackConversationFilterInclude::Private,
1760 ])
1761 .with_exclude_bot_users(true),
1762 );
1763
1764 let json = serde_json::to_string(&elem)?;
1765 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1766 assert_eq!(elem, parsed);
1767 Ok(())
1768 }
1769
1770 #[test]
1771 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1772 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1773
1774 let json = serde_json::to_value(&elem)?;
1775 assert!(json.get("filter").is_none());
1776 Ok(())
1777 }
1778
1779 #[test]
1780 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1781 let payload = include_str!("./fixtures/slack_image_blocks.json");
1782 let content: SlackMessageContent = serde_json::from_str(payload)?;
1783 let blocks = content.blocks.expect("Blocks should not be empty");
1784 match blocks.first() {
1785 Some(SlackBlock::Section(section)) => match §ion.accessory {
1786 Some(SlackSectionBlockElement::Image(image)) => {
1787 assert_eq!(image.alt_text, "alt text for image");
1788 match &image.image_url_or_file {
1789 SlackImageUrlOrFile::ImageUrl { image_url } => {
1790 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1791 }
1792 SlackImageUrlOrFile::SlackFile { slack_file } => {
1793 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1794 }
1795 }
1796 }
1797 _ => panic!("Expected a section block with an image accessory"),
1798 },
1799 _ => panic!("Expected a section block"),
1800 }
1801 Ok(())
1802 }
1803
1804 #[test]
1805 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1806 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1807 let block: SlackBlock = serde_json::from_str(payload)?;
1808
1809 let rich = match block {
1810 SlackBlock::RichText(r) => r,
1811 _ => panic!("Expected a RichText block"),
1812 };
1813
1814 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1815 assert_eq!(rich.elements.len(), 4);
1816
1817 let section = match &rich.elements[0] {
1819 SlackRichTextElement::Section(s) => s,
1820 _ => panic!("Expected a Section element"),
1821 };
1822 assert_eq!(section.elements.len(), 7);
1823
1824 let text = match §ion.elements[0] {
1826 SlackRichTextInlineElement::Text(t) => t,
1827 _ => panic!("Expected a Text element"),
1828 };
1829 assert_eq!(text.text, "Hello ");
1830 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
1831
1832 assert!(matches!(
1834 §ion.elements[1],
1835 SlackRichTextInlineElement::User(_)
1836 ));
1837
1838 let emoji = match §ion.elements[4] {
1840 SlackRichTextInlineElement::Emoji(e) => e,
1841 _ => panic!("Expected an Emoji element"),
1842 };
1843 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
1844
1845 let list = match &rich.elements[1] {
1847 SlackRichTextElement::List(l) => l,
1848 _ => panic!("Expected a List element"),
1849 };
1850 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
1851 assert_eq!(list.elements.len(), 2);
1852
1853 assert!(matches!(
1855 &list.elements[0],
1856 SlackRichTextListElement::Section(_)
1857 ));
1858 assert!(matches!(
1859 &list.elements[1],
1860 SlackRichTextListElement::Section(_)
1861 ));
1862
1863 assert!(matches!(
1865 &rich.elements[2],
1866 SlackRichTextElement::Preformatted(_)
1867 ));
1868
1869 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
1871
1872 Ok(())
1873 }
1874
1875 #[test]
1876 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1877 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1878 let block: SlackBlock = serde_json::from_str(payload)?;
1879 let serialized = serde_json::to_string(&block)?;
1880 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1881 assert_eq!(block, block2);
1882 Ok(())
1883 }
1884
1885 #[test]
1886 fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1887 let payload = include_str!("./fixtures/slack_table_block.json");
1888 let block: SlackBlock = serde_json::from_str(payload)?;
1889
1890 let table = match block {
1891 SlackBlock::Table(t) => t,
1892 _ => panic!("Expected a Table block"),
1893 };
1894
1895 assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
1896 assert_eq!(table.rows.len(), 2);
1897 assert_eq!(table.rows[0].len(), 2);
1898
1899 match &table.rows[0][0] {
1901 SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
1902 _ => panic!("Expected RawText cell"),
1903 }
1904
1905 match &table.rows[1][1] {
1907 SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
1908 _ => panic!("Expected RichText cell"),
1909 }
1910
1911 let settings = table
1912 .column_settings
1913 .expect("column_settings should be present");
1914 assert_eq!(settings.len(), 2);
1915 assert_eq!(settings[0].is_wrapped, Some(true));
1916 assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
1917
1918 Ok(())
1919 }
1920
1921 #[test]
1922 fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1923 let payload = include_str!("./fixtures/slack_table_block.json");
1924 let block: SlackBlock = serde_json::from_str(payload)?;
1925 let serialized = serde_json::to_string(&block)?;
1926 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1927 assert_eq!(block, block2);
1928 Ok(())
1929 }
1930
1931 #[test]
1932 fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1933 let payload = include_str!("./fixtures/slack_task_card_block.json");
1934 let block: SlackBlock = serde_json::from_str(payload)?;
1935
1936 let task_card = match block {
1937 SlackBlock::TaskCard(t) => t,
1938 _ => panic!("Expected a TaskCard block"),
1939 };
1940
1941 assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
1942 assert_eq!(task_card.title, "Fetching weather data");
1943 assert_eq!(
1944 task_card.block_id,
1945 Some(SlackBlockId("task_card_block_1".into()))
1946 );
1947 assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
1948
1949 let output = task_card.output.expect("output should be present");
1950 let output_block = match output {
1951 SlackRichTextInlineContent::RichText(b) => b,
1952 };
1953 assert_eq!(output_block.elements.len(), 1);
1954
1955 let sources = task_card.sources.expect("sources should be present");
1956 assert_eq!(sources.len(), 2);
1957 match &sources[0] {
1958 SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
1959 }
1960
1961 Ok(())
1962 }
1963
1964 #[test]
1965 fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1966 let payload = include_str!("./fixtures/slack_task_card_block.json");
1967 let block: SlackBlock = serde_json::from_str(payload)?;
1968 let serialized = serde_json::to_string(&block)?;
1969 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1970 assert_eq!(block, block2);
1971 Ok(())
1972 }
1973
1974 #[test]
1975 fn test_slack_alert_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1976 let payload = include_str!("./fixtures/slack_alert_block.json");
1977 let block: SlackBlock = serde_json::from_str(payload)?;
1978 match block {
1979 SlackBlock::Alert(alert) => {
1980 assert_eq!(alert.level, Some(SlackAlertLevel::Warning));
1981 }
1982 _ => panic!("Expected Alert block"),
1983 }
1984 Ok(())
1985 }
1986
1987 #[test]
1988 fn test_slack_alert_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1989 let payload = include_str!("./fixtures/slack_alert_block.json");
1990 let block: SlackBlock = serde_json::from_str(payload)?;
1991 let serialized = serde_json::to_string(&block)?;
1992 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1993 assert_eq!(block, block2);
1994 Ok(())
1995 }
1996
1997 #[test]
1998 fn test_slack_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1999 let payload = include_str!("./fixtures/slack_card_block.json");
2000 let block: SlackBlock = serde_json::from_str(payload)?;
2001 match block {
2002 SlackBlock::Card(card) => {
2003 assert!(card.hero_image.is_some());
2004 assert!(card.actions.is_some());
2005 let actions = card.actions.unwrap();
2006 assert_eq!(actions.len(), 1);
2007 match &actions[0] {
2008 SlackCardActionBlockElement::Button(btn) => {
2009 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2010 }
2011 }
2012 }
2013 _ => panic!("Expected Card block"),
2014 }
2015 Ok(())
2016 }
2017
2018 #[test]
2019 fn test_slack_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2020 let payload = include_str!("./fixtures/slack_card_block.json");
2021 let block: SlackBlock = serde_json::from_str(payload)?;
2022 let serialized = serde_json::to_string(&block)?;
2023 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2024 assert_eq!(block, block2);
2025 Ok(())
2026 }
2027
2028 #[test]
2029 fn test_slack_context_actions_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2030 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2031 let block: SlackBlock = serde_json::from_str(payload)?;
2032 match block {
2033 SlackBlock::ContextActions(ctx) => {
2034 assert_eq!(ctx.elements.len(), 1);
2035 match &ctx.elements[0] {
2036 SlackContextActionBlockElement::IconButton(btn) => {
2037 assert_eq!(btn.icon, "trash");
2038 }
2039 _ => panic!("Expected IconButton element"),
2040 }
2041 }
2042 _ => panic!("Expected ContextActions block"),
2043 }
2044 Ok(())
2045 }
2046
2047 #[test]
2048 fn test_slack_context_actions_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2049 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2050 let block: SlackBlock = serde_json::from_str(payload)?;
2051 let serialized = serde_json::to_string(&block)?;
2052 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2053 assert_eq!(block, block2);
2054 Ok(())
2055 }
2056
2057 #[test]
2058 fn test_slack_workflow_button_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2059 let payload = include_str!("./fixtures/slack_workflow_button.json");
2060 let block: SlackBlock = serde_json::from_str(payload)?;
2061 match block {
2062 SlackBlock::Actions(actions) => {
2063 assert_eq!(actions.elements.len(), 1);
2064 match &actions.elements[0] {
2065 SlackActionBlockElement::WorkflowButton(btn) => {
2066 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2067 let params = btn
2068 .workflow
2069 .trigger
2070 .customizable_input_parameters
2071 .as_ref()
2072 .expect("params should be present");
2073 assert_eq!(params.len(), 1);
2074 assert_eq!(params[0].name, "user_input");
2075 }
2076 _ => panic!("Expected WorkflowButton element"),
2077 }
2078 }
2079 _ => panic!("Expected Actions block"),
2080 }
2081 Ok(())
2082 }
2083
2084 #[test]
2085 fn test_slack_workflow_button_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2086 let payload = include_str!("./fixtures/slack_workflow_button.json");
2087 let block: SlackBlock = serde_json::from_str(payload)?;
2088 let serialized = serde_json::to_string(&block)?;
2089 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2090 assert_eq!(block, block2);
2091 Ok(())
2092 }
2093}