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 #[serde(rename = "message_mention")]
1263 MessageMention(SlackRichTextMessageMention),
1264 #[serde(untagged)]
1265 Unknown(serde_json::Value),
1266}
1267
1268#[skip_serializing_none]
1269#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1270pub struct SlackRichTextStyle {
1271 pub bold: Option<bool>,
1272 pub italic: Option<bool>,
1273 pub strike: Option<bool>,
1274 pub code: Option<bool>,
1275 pub underline: Option<bool>,
1276 pub highlight: Option<bool>,
1277 pub client_highlight: Option<bool>,
1278 pub unlink: Option<bool>,
1279}
1280
1281#[skip_serializing_none]
1282#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1283pub struct SlackRichTextText {
1284 pub text: String,
1285 pub style: Option<SlackRichTextStyle>,
1286}
1287
1288#[skip_serializing_none]
1289#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1290pub struct SlackRichTextLink {
1291 pub url: SlackRelaxedUrl,
1292 pub text: Option<String>,
1293 #[serde(rename = "unsafe")]
1294 pub unsafe_: Option<bool>,
1295 pub style: Option<SlackRichTextStyle>,
1296}
1297
1298#[skip_serializing_none]
1299#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1300pub struct SlackRichTextUser {
1301 pub user_id: SlackUserId,
1302 pub style: Option<SlackRichTextStyle>,
1303}
1304
1305#[skip_serializing_none]
1306#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1307pub struct SlackRichTextChannel {
1308 pub channel_id: SlackChannelId,
1309 pub style: Option<SlackRichTextStyle>,
1310}
1311
1312#[skip_serializing_none]
1313#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1314pub struct SlackRichTextUserGroup {
1315 pub usergroup_id: SlackUserGroupId,
1316 pub style: Option<SlackRichTextStyle>,
1317}
1318
1319#[skip_serializing_none]
1320#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1321pub struct SlackRichTextEmoji {
1322 pub name: SlackEmojiName,
1323 pub unicode: Option<String>,
1324}
1325
1326#[skip_serializing_none]
1327#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1328pub struct SlackRichTextDate {
1329 pub timestamp: SlackDateTime,
1330 pub format: String,
1331 pub fallback: Option<String>,
1332 pub style: Option<SlackRichTextStyle>,
1333}
1334
1335#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1336#[serde(rename_all = "snake_case")]
1337pub enum SlackRichTextBroadcastRange {
1338 Here,
1339 Channel,
1340 Everyone,
1341}
1342
1343#[skip_serializing_none]
1344#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1345pub struct SlackRichTextBroadcast {
1346 pub range: SlackRichTextBroadcastRange,
1347 pub style: Option<SlackRichTextStyle>,
1348}
1349
1350#[skip_serializing_none]
1351#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1352pub struct SlackRichTextColor {
1353 pub value: String,
1354}
1355
1356#[skip_serializing_none]
1357#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1358pub struct SlackRichTextMessageMention {
1359 pub url: SlackRelaxedUrl,
1360 pub text: Option<String>,
1361 pub channel_id: Option<SlackChannelId>,
1362 pub author_id: Option<SlackUserId>,
1363 pub message_ts: Option<SlackTs>,
1364 pub thread_ts: Option<SlackTs>,
1365 pub style: Option<SlackRichTextStyle>,
1366}
1367
1368#[skip_serializing_none]
1372#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1373pub struct SlackBlockRichTextInputElement {
1374 pub action_id: SlackActionId,
1375 pub initial_value: Option<SlackRichTextBlock>,
1376 pub focus_on_load: Option<bool>,
1377 pub placeholder: Option<SlackBlockPlainTextOnly>,
1378}
1379
1380impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1381 fn from(element: SlackBlockRichTextInputElement) -> Self {
1382 SlackInputBlockElement::RichTextInput(element)
1383 }
1384}
1385
1386#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1387#[serde(untagged)]
1388pub enum SlackImageUrlOrFile {
1389 ImageUrl { image_url: Url },
1390 SlackFile { slack_file: SlackFileIdOrUrl },
1391}
1392
1393impl SlackImageUrlOrFile {
1394 pub fn image_url(&self) -> Option<&Url> {
1395 match self {
1396 SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1397 SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1398 SlackFileIdOrUrl::Url { url } => Some(url),
1399 _ => None,
1400 },
1401 }
1402 }
1403}
1404
1405impl From<Url> for SlackImageUrlOrFile {
1406 fn from(value: Url) -> Self {
1407 SlackImageUrlOrFile::ImageUrl { image_url: value }
1408 }
1409}
1410
1411#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1412#[serde(untagged)]
1413pub enum SlackFileIdOrUrl {
1414 Id { id: SlackFileId },
1415 Url { url: Url },
1416}
1417
1418impl From<SlackFileId> for SlackFileIdOrUrl {
1419 fn from(value: SlackFileId) -> Self {
1420 SlackFileIdOrUrl::Id { id: value }
1421 }
1422}
1423
1424#[skip_serializing_none]
1428#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1429pub struct SlackTableBlock {
1430 pub block_id: Option<SlackBlockId>,
1431 pub rows: Vec<Vec<SlackTableCell>>,
1432 pub column_settings: Option<Vec<SlackTableColumnSetting>>,
1433}
1434
1435impl From<SlackTableBlock> for SlackBlock {
1436 fn from(block: SlackTableBlock) -> Self {
1437 SlackBlock::Table(block)
1438 }
1439}
1440
1441#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1442#[serde(tag = "type")]
1443pub enum SlackTableCell {
1444 #[serde(rename = "raw_text")]
1445 RawText(SlackTableRawTextCell),
1446 #[serde(rename = "rich_text")]
1447 RichText(SlackTableRichTextCell),
1448}
1449
1450#[skip_serializing_none]
1451#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1452pub struct SlackTableRawTextCell {
1453 pub text: String,
1454}
1455
1456#[skip_serializing_none]
1457#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1458pub struct SlackTableRichTextCell {
1459 pub elements: Vec<SlackRichTextElement>,
1460}
1461
1462#[skip_serializing_none]
1463#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1464pub struct SlackTableColumnSetting {
1465 pub align: Option<SlackTableColumnAlign>,
1466 pub is_wrapped: Option<bool>,
1467}
1468
1469#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1470#[serde(rename_all = "snake_case")]
1471pub enum SlackTableColumnAlign {
1472 Left,
1473 Center,
1474 Right,
1475}
1476
1477#[skip_serializing_none]
1481#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1482pub struct SlackTaskCardBlock {
1483 pub task_id: SlackTaskId,
1484 pub title: String,
1485 pub block_id: Option<SlackBlockId>,
1486 pub status: Option<SlackTaskCardStatus>,
1487 #[serde(rename = "details")]
1488 pub details: Option<SlackRichTextInlineContent>,
1489 #[serde(rename = "output")]
1490 pub output: Option<SlackRichTextInlineContent>,
1491 pub sources: Option<Vec<SlackTaskCardSource>>,
1492}
1493
1494impl From<SlackTaskCardBlock> for SlackBlock {
1495 fn from(block: SlackTaskCardBlock) -> Self {
1496 SlackBlock::TaskCard(block)
1497 }
1498}
1499
1500#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1501#[serde(rename_all = "snake_case")]
1502pub enum SlackTaskCardStatus {
1503 Pending,
1504 InProgress,
1505 Complete,
1506 Error,
1507}
1508
1509#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1513pub struct SlackUrlSourceElement {
1514 pub url: Url,
1515 pub text: String,
1516}
1517
1518#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1519#[serde(tag = "type")]
1520pub enum SlackTaskCardSource {
1521 #[serde(rename = "url")]
1522 Url(SlackUrlSourceElement),
1523}
1524
1525impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1526 fn from(element: SlackUrlSourceElement) -> Self {
1527 SlackTaskCardSource::Url(element)
1528 }
1529}
1530
1531#[skip_serializing_none]
1535#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1536pub struct SlackBlockFileInputElement {
1537 pub action_id: SlackActionId,
1538 pub filetypes: Option<Vec<String>>,
1539 pub max_files: Option<u64>,
1540}
1541
1542impl From<SlackBlockFileInputElement> for SlackInputBlockElement {
1543 fn from(element: SlackBlockFileInputElement) -> Self {
1544 SlackInputBlockElement::FileInput(element)
1545 }
1546}
1547
1548#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1552#[serde(rename_all = "snake_case")]
1553pub enum SlackAlertLevel {
1554 Warning,
1555 Error,
1556 Info,
1557 Success,
1558}
1559
1560#[skip_serializing_none]
1561#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1562pub struct SlackAlertBlock {
1563 pub block_id: Option<SlackBlockId>,
1564 pub text: SlackBlockText,
1565 pub level: Option<SlackAlertLevel>,
1566}
1567
1568impl From<SlackAlertBlock> for SlackBlock {
1569 fn from(block: SlackAlertBlock) -> Self {
1570 SlackBlock::Alert(block)
1571 }
1572}
1573
1574#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1578#[serde(tag = "type")]
1579pub enum SlackCardImageElement {
1580 #[serde(rename = "image")]
1581 Image(SlackBlockImageElement),
1582}
1583
1584impl From<SlackBlockImageElement> for SlackCardImageElement {
1585 fn from(element: SlackBlockImageElement) -> Self {
1586 SlackCardImageElement::Image(element)
1587 }
1588}
1589
1590#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1591#[serde(tag = "type")]
1592pub enum SlackCardActionBlockElement {
1593 #[serde(rename = "button")]
1594 Button(SlackBlockButtonElement),
1595}
1596
1597impl From<SlackBlockButtonElement> for SlackCardActionBlockElement {
1598 fn from(element: SlackBlockButtonElement) -> Self {
1599 SlackCardActionBlockElement::Button(element)
1600 }
1601}
1602
1603#[skip_serializing_none]
1604#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1605pub struct SlackCardBlock {
1606 pub block_id: Option<SlackBlockId>,
1607 pub title: Option<SlackBlockText>,
1608 pub subtitle: Option<SlackBlockText>,
1609 pub body: Option<SlackBlockText>,
1610 pub hero_image: Option<SlackCardImageElement>,
1611 pub icon: Option<SlackCardImageElement>,
1612 pub actions: Option<Vec<SlackCardActionBlockElement>>,
1613}
1614
1615impl From<SlackCardBlock> for SlackBlock {
1616 fn from(block: SlackCardBlock) -> Self {
1617 SlackBlock::Card(block)
1618 }
1619}
1620
1621#[skip_serializing_none]
1625#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1626pub struct SlackCarouselBlock {
1627 pub block_id: Option<SlackBlockId>,
1628 pub elements: Vec<SlackBlock>,
1629}
1630
1631impl From<SlackCarouselBlock> for SlackBlock {
1632 fn from(block: SlackCarouselBlock) -> Self {
1633 SlackBlock::Carousel(block)
1634 }
1635}
1636
1637#[skip_serializing_none]
1643#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1644pub struct SlackFeedbackButtonItem {
1645 pub action_id: SlackActionId,
1646 pub value: String,
1647 pub text: SlackBlockPlainTextOnly,
1648 pub confirm: Option<SlackBlockConfirmItem>,
1649}
1650
1651#[skip_serializing_none]
1652#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1653pub struct SlackBlockFeedbackButtonsElement {
1654 pub action_id: SlackActionId,
1655 pub positive: SlackFeedbackButtonItem,
1656 pub negative: SlackFeedbackButtonItem,
1657}
1658
1659#[skip_serializing_none]
1660#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1661pub struct SlackBlockIconButtonElement {
1662 pub action_id: SlackActionId,
1663 pub icon: String,
1664 pub text: SlackBlockPlainTextOnly,
1665 pub value: Option<String>,
1666 pub confirm: Option<SlackBlockConfirmItem>,
1667 pub accessibility_label: Option<SlackAccessibilityLabel>,
1668 pub visible_to_user_ids: Option<Vec<SlackUserId>>,
1669}
1670
1671#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1672#[serde(tag = "type")]
1673pub enum SlackContextActionBlockElement {
1674 #[serde(rename = "feedback_buttons")]
1675 FeedbackButtons(SlackBlockFeedbackButtonsElement),
1676 #[serde(rename = "icon_button")]
1677 IconButton(SlackBlockIconButtonElement),
1678}
1679
1680impl From<SlackBlockFeedbackButtonsElement> for SlackContextActionBlockElement {
1681 fn from(element: SlackBlockFeedbackButtonsElement) -> Self {
1682 SlackContextActionBlockElement::FeedbackButtons(element)
1683 }
1684}
1685
1686impl From<SlackBlockIconButtonElement> for SlackContextActionBlockElement {
1687 fn from(element: SlackBlockIconButtonElement) -> Self {
1688 SlackContextActionBlockElement::IconButton(element)
1689 }
1690}
1691
1692#[skip_serializing_none]
1693#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1694pub struct SlackContextActionsBlock {
1695 pub block_id: Option<SlackBlockId>,
1696 pub elements: Vec<SlackContextActionBlockElement>,
1697}
1698
1699impl From<SlackContextActionsBlock> for SlackBlock {
1700 fn from(block: SlackContextActionsBlock) -> Self {
1701 SlackBlock::ContextActions(block)
1702 }
1703}
1704
1705#[cfg(test)]
1706mod test {
1707 use super::*;
1708 use crate::blocks::SlackHomeView;
1709
1710 #[test]
1711 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1712 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1713 let block: SlackBlock = serde_json::from_str(payload)?;
1714 match block {
1715 SlackBlock::Section(section) => match section.accessory {
1716 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1717 let filter = elem.filter.expect("filter should be present");
1718 let include = filter.include.expect("include should be present");
1719 assert_eq!(include.len(), 2);
1720 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1721 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1722 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1723 assert_eq!(filter.exclude_bot_users, Some(true));
1724 }
1725 _ => panic!("Expected ConversationsSelect accessory"),
1726 },
1727 _ => panic!("Expected Section block"),
1728 }
1729 Ok(())
1730 }
1731
1732 #[test]
1733 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1734 let filter = SlackBlockConversationFilter::new()
1735 .with_include(vec![
1736 SlackConversationFilterInclude::Im,
1737 SlackConversationFilterInclude::Mpim,
1738 ])
1739 .with_exclude_bot_users(true);
1740
1741 let json = serde_json::to_value(&filter)?;
1742 assert_eq!(
1743 json,
1744 serde_json::json!({
1745 "include": ["im", "mpim"],
1746 "exclude_bot_users": true
1747 })
1748 );
1749 Ok(())
1750 }
1751
1752 #[test]
1753 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1754 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1755 .with_filter(
1756 SlackBlockConversationFilter::new()
1757 .with_include(vec![SlackConversationFilterInclude::Public])
1758 .with_exclude_external_shared_channels(true),
1759 );
1760
1761 let json = serde_json::to_string(&elem)?;
1762 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1763 assert_eq!(elem, parsed);
1764 Ok(())
1765 }
1766
1767 #[test]
1768 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1769 let elem =
1770 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1771 .with_filter(
1772 SlackBlockConversationFilter::new()
1773 .with_include(vec![
1774 SlackConversationFilterInclude::Public,
1775 SlackConversationFilterInclude::Private,
1776 ])
1777 .with_exclude_bot_users(true),
1778 );
1779
1780 let json = serde_json::to_string(&elem)?;
1781 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1782 assert_eq!(elem, parsed);
1783 Ok(())
1784 }
1785
1786 #[test]
1787 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1788 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1789
1790 let json = serde_json::to_value(&elem)?;
1791 assert!(json.get("filter").is_none());
1792 Ok(())
1793 }
1794
1795 #[test]
1796 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1797 let payload = include_str!("./fixtures/slack_image_blocks.json");
1798 let content: SlackMessageContent = serde_json::from_str(payload)?;
1799 let blocks = content.blocks.expect("Blocks should not be empty");
1800 match blocks.first() {
1801 Some(SlackBlock::Section(section)) => match §ion.accessory {
1802 Some(SlackSectionBlockElement::Image(image)) => {
1803 assert_eq!(image.alt_text, "alt text for image");
1804 match &image.image_url_or_file {
1805 SlackImageUrlOrFile::ImageUrl { image_url } => {
1806 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1807 }
1808 SlackImageUrlOrFile::SlackFile { slack_file } => {
1809 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1810 }
1811 }
1812 }
1813 _ => panic!("Expected a section block with an image accessory"),
1814 },
1815 _ => panic!("Expected a section block"),
1816 }
1817 Ok(())
1818 }
1819
1820 #[test]
1821 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1822 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1823 let block: SlackBlock = serde_json::from_str(payload)?;
1824
1825 let rich = match block {
1826 SlackBlock::RichText(r) => r,
1827 _ => panic!("Expected a RichText block"),
1828 };
1829
1830 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1831 assert_eq!(rich.elements.len(), 4);
1832
1833 let section = match &rich.elements[0] {
1835 SlackRichTextElement::Section(s) => s,
1836 _ => panic!("Expected a Section element"),
1837 };
1838 assert_eq!(section.elements.len(), 7);
1839
1840 let text = match §ion.elements[0] {
1842 SlackRichTextInlineElement::Text(t) => t,
1843 _ => panic!("Expected a Text element"),
1844 };
1845 assert_eq!(text.text, "Hello ");
1846 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
1847
1848 assert!(matches!(
1850 §ion.elements[1],
1851 SlackRichTextInlineElement::User(_)
1852 ));
1853
1854 let emoji = match §ion.elements[4] {
1856 SlackRichTextInlineElement::Emoji(e) => e,
1857 _ => panic!("Expected an Emoji element"),
1858 };
1859 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
1860
1861 let list = match &rich.elements[1] {
1863 SlackRichTextElement::List(l) => l,
1864 _ => panic!("Expected a List element"),
1865 };
1866 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
1867 assert_eq!(list.elements.len(), 2);
1868
1869 assert!(matches!(
1871 &list.elements[0],
1872 SlackRichTextListElement::Section(_)
1873 ));
1874 assert!(matches!(
1875 &list.elements[1],
1876 SlackRichTextListElement::Section(_)
1877 ));
1878
1879 assert!(matches!(
1881 &rich.elements[2],
1882 SlackRichTextElement::Preformatted(_)
1883 ));
1884
1885 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
1887
1888 Ok(())
1889 }
1890
1891 #[test]
1892 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1893 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1894 let block: SlackBlock = serde_json::from_str(payload)?;
1895 let serialized = serde_json::to_string(&block)?;
1896 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1897 assert_eq!(block, block2);
1898 Ok(())
1899 }
1900
1901 #[test]
1902 fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1903 let payload = include_str!("./fixtures/slack_table_block.json");
1904 let block: SlackBlock = serde_json::from_str(payload)?;
1905
1906 let table = match block {
1907 SlackBlock::Table(t) => t,
1908 _ => panic!("Expected a Table block"),
1909 };
1910
1911 assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
1912 assert_eq!(table.rows.len(), 2);
1913 assert_eq!(table.rows[0].len(), 2);
1914
1915 match &table.rows[0][0] {
1917 SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
1918 _ => panic!("Expected RawText cell"),
1919 }
1920
1921 match &table.rows[1][1] {
1923 SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
1924 _ => panic!("Expected RichText cell"),
1925 }
1926
1927 let settings = table
1928 .column_settings
1929 .expect("column_settings should be present");
1930 assert_eq!(settings.len(), 2);
1931 assert_eq!(settings[0].is_wrapped, Some(true));
1932 assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
1933
1934 Ok(())
1935 }
1936
1937 #[test]
1938 fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1939 let payload = include_str!("./fixtures/slack_table_block.json");
1940 let block: SlackBlock = serde_json::from_str(payload)?;
1941 let serialized = serde_json::to_string(&block)?;
1942 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1943 assert_eq!(block, block2);
1944 Ok(())
1945 }
1946
1947 #[test]
1948 fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1949 let payload = include_str!("./fixtures/slack_task_card_block.json");
1950 let block: SlackBlock = serde_json::from_str(payload)?;
1951
1952 let task_card = match block {
1953 SlackBlock::TaskCard(t) => t,
1954 _ => panic!("Expected a TaskCard block"),
1955 };
1956
1957 assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
1958 assert_eq!(task_card.title, "Fetching weather data");
1959 assert_eq!(
1960 task_card.block_id,
1961 Some(SlackBlockId("task_card_block_1".into()))
1962 );
1963 assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
1964
1965 let output = task_card.output.expect("output should be present");
1966 let output_block = match output {
1967 SlackRichTextInlineContent::RichText(b) => b,
1968 };
1969 assert_eq!(output_block.elements.len(), 1);
1970
1971 let sources = task_card.sources.expect("sources should be present");
1972 assert_eq!(sources.len(), 2);
1973 match &sources[0] {
1974 SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
1975 }
1976
1977 Ok(())
1978 }
1979
1980 #[test]
1981 fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1982 let payload = include_str!("./fixtures/slack_task_card_block.json");
1983 let block: SlackBlock = serde_json::from_str(payload)?;
1984 let serialized = serde_json::to_string(&block)?;
1985 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1986 assert_eq!(block, block2);
1987 Ok(())
1988 }
1989
1990 #[test]
1991 fn test_slack_alert_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1992 let payload = include_str!("./fixtures/slack_alert_block.json");
1993 let block: SlackBlock = serde_json::from_str(payload)?;
1994 match block {
1995 SlackBlock::Alert(alert) => {
1996 assert_eq!(alert.level, Some(SlackAlertLevel::Warning));
1997 }
1998 _ => panic!("Expected Alert block"),
1999 }
2000 Ok(())
2001 }
2002
2003 #[test]
2004 fn test_slack_alert_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2005 let payload = include_str!("./fixtures/slack_alert_block.json");
2006 let block: SlackBlock = serde_json::from_str(payload)?;
2007 let serialized = serde_json::to_string(&block)?;
2008 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2009 assert_eq!(block, block2);
2010 Ok(())
2011 }
2012
2013 #[test]
2014 fn test_slack_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2015 let payload = include_str!("./fixtures/slack_card_block.json");
2016 let block: SlackBlock = serde_json::from_str(payload)?;
2017 match block {
2018 SlackBlock::Card(card) => {
2019 assert!(card.hero_image.is_some());
2020 assert!(card.actions.is_some());
2021 let actions = card.actions.unwrap();
2022 assert_eq!(actions.len(), 1);
2023 match &actions[0] {
2024 SlackCardActionBlockElement::Button(btn) => {
2025 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2026 }
2027 }
2028 }
2029 _ => panic!("Expected Card block"),
2030 }
2031 Ok(())
2032 }
2033
2034 #[test]
2035 fn test_slack_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2036 let payload = include_str!("./fixtures/slack_card_block.json");
2037 let block: SlackBlock = serde_json::from_str(payload)?;
2038 let serialized = serde_json::to_string(&block)?;
2039 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2040 assert_eq!(block, block2);
2041 Ok(())
2042 }
2043
2044 #[test]
2045 fn test_slack_context_actions_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2046 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2047 let block: SlackBlock = serde_json::from_str(payload)?;
2048 match block {
2049 SlackBlock::ContextActions(ctx) => {
2050 assert_eq!(ctx.elements.len(), 1);
2051 match &ctx.elements[0] {
2052 SlackContextActionBlockElement::IconButton(btn) => {
2053 assert_eq!(btn.icon, "trash");
2054 }
2055 _ => panic!("Expected IconButton element"),
2056 }
2057 }
2058 _ => panic!("Expected ContextActions block"),
2059 }
2060 Ok(())
2061 }
2062
2063 #[test]
2064 fn test_slack_context_actions_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2065 let payload = include_str!("./fixtures/slack_context_actions_block.json");
2066 let block: SlackBlock = serde_json::from_str(payload)?;
2067 let serialized = serde_json::to_string(&block)?;
2068 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2069 assert_eq!(block, block2);
2070 Ok(())
2071 }
2072
2073 #[test]
2074 fn test_slack_workflow_button_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2075 let payload = include_str!("./fixtures/slack_workflow_button.json");
2076 let block: SlackBlock = serde_json::from_str(payload)?;
2077 match block {
2078 SlackBlock::Actions(actions) => {
2079 assert_eq!(actions.elements.len(), 1);
2080 match &actions.elements[0] {
2081 SlackActionBlockElement::WorkflowButton(btn) => {
2082 assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2083 let params = btn
2084 .workflow
2085 .trigger
2086 .customizable_input_parameters
2087 .as_ref()
2088 .expect("params should be present");
2089 assert_eq!(params.len(), 1);
2090 assert_eq!(params[0].name, "user_input");
2091 }
2092 _ => panic!("Expected WorkflowButton element"),
2093 }
2094 }
2095 _ => panic!("Expected Actions block"),
2096 }
2097 Ok(())
2098 }
2099
2100 #[test]
2101 fn test_slack_workflow_button_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2102 let payload = include_str!("./fixtures/slack_workflow_button.json");
2103 let block: SlackBlock = serde_json::from_str(payload)?;
2104 let serialized = serde_json::to_string(&block)?;
2105 let block2: SlackBlock = serde_json::from_str(&serialized)?;
2106 assert_eq!(block, block2);
2107 Ok(())
2108 }
2109
2110 #[test]
2111 fn test_rich_text_message_mention_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2112 let payload = serde_json::json!({
2113 "type": "rich_text",
2114 "block_id": "msgm1",
2115 "elements": [
2116 {
2117 "type": "rich_text_section",
2118 "elements": [
2119 {
2120 "type": "message_mention",
2121 "url": "https://acme.slack.com/archives/C12345678/p1784153496441789?thread_ts=1784153496.441789&cid=C12345678",
2122 "text": "a message",
2123 "channel_id": "C12345678",
2124 "author_id": "U12345678",
2125 "message_ts": "1784153496.441789",
2126 "thread_ts": "1784153496.441789"
2127 }
2128 ]
2129 }
2130 ]
2131 })
2132 .to_string();
2133 let block: SlackBlock = serde_json::from_str(&payload)?;
2134 match block {
2135 SlackBlock::RichText(rich_text) => match &rich_text.elements[0] {
2136 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2137 SlackRichTextInlineElement::MessageMention(mention) => {
2138 assert_eq!(mention.channel_id, Some(SlackChannelId("C12345678".into())));
2139 assert_eq!(mention.author_id, Some(SlackUserId("U12345678".into())));
2140 assert_eq!(
2141 mention.message_ts,
2142 Some(SlackTs("1784153496.441789".into()))
2143 );
2144 }
2145 other => panic!("Expected MessageMention element, got {other:?}"),
2146 },
2147 _ => panic!("Expected Section element"),
2148 },
2149 _ => panic!("Expected RichText block"),
2150 }
2151 Ok(())
2152 }
2153
2154 #[test]
2155 fn test_rich_text_unknown_inline_element_deserialize() -> Result<(), Box<dyn std::error::Error>>
2156 {
2157 let payload = serde_json::json!({
2158 "type": "rich_text_section",
2159 "elements": [
2160 {
2161 "type": "some_future_element",
2162 "foo": "bar"
2163 }
2164 ]
2165 })
2166 .to_string();
2167 let section: SlackRichTextElement = serde_json::from_str(&payload)?;
2168 match section {
2169 SlackRichTextElement::Section(section) => match §ion.elements[0] {
2170 SlackRichTextInlineElement::Unknown(value) => {
2171 assert_eq!(value["type"], "some_future_element");
2172 }
2173 other => panic!("Expected Unknown element, got {other:?}"),
2174 },
2175 _ => panic!("Expected Section element"),
2176 }
2177 Ok(())
2178 }
2179}