1use rsb_derive::Builder;
2use rvstruct::ValueStruct;
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5use url::Url;
6
7use crate::*;
8
9#[skip_serializing_none]
10#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
11pub struct SlackBlockId(pub String);
12
13#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
14pub struct SlackTaskId(pub String);
15
16#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
17#[serde(tag = "type")]
18pub enum SlackBlock {
19 #[serde(rename = "section")]
20 Section(SlackSectionBlock),
21 #[serde(rename = "header")]
22 Header(SlackHeaderBlock),
23 #[serde(rename = "divider")]
24 Divider(SlackDividerBlock),
25 #[serde(rename = "image")]
26 Image(SlackImageBlock),
27 #[serde(rename = "actions")]
28 Actions(SlackActionsBlock),
29 #[serde(rename = "context")]
30 Context(SlackContextBlock),
31 #[serde(rename = "input")]
32 Input(SlackInputBlock),
33 #[serde(rename = "file")]
34 File(SlackFileBlock),
35 #[serde(rename = "video")]
36 Video(SlackVideoBlock),
37 #[serde(rename = "markdown")]
38 Markdown(SlackMarkdownBlock),
39 #[serde(rename = "rich_text")]
40 RichText(SlackRichTextBlock),
41 #[serde(rename = "table")]
42 Table(SlackTableBlock),
43 #[serde(rename = "task_card")]
44 TaskCard(SlackTaskCardBlock),
45 #[serde(rename = "share_shortcut")]
46 ShareShortcut(serde_json::Value),
47 #[serde(rename = "event")]
48 Event(serde_json::Value),
49}
50
51#[skip_serializing_none]
52#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
53pub struct SlackSectionBlock {
54 pub block_id: Option<SlackBlockId>,
55 pub text: Option<SlackBlockText>,
56 pub fields: Option<Vec<SlackBlockText>>,
57 pub accessory: Option<SlackSectionBlockElement>,
58}
59
60impl From<SlackSectionBlock> for SlackBlock {
61 fn from(block: SlackSectionBlock) -> Self {
62 SlackBlock::Section(block)
63 }
64}
65
66#[skip_serializing_none]
67#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
68pub struct SlackHeaderBlock {
69 pub block_id: Option<SlackBlockId>,
70 pub text: SlackBlockPlainTextOnly,
71}
72
73impl From<SlackHeaderBlock> for SlackBlock {
74 fn from(block: SlackHeaderBlock) -> Self {
75 SlackBlock::Header(block)
76 }
77}
78
79#[skip_serializing_none]
80#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
81pub struct SlackDividerBlock {
82 pub block_id: Option<SlackBlockId>,
83}
84
85impl From<SlackDividerBlock> for SlackBlock {
86 fn from(block: SlackDividerBlock) -> Self {
87 SlackBlock::Divider(block)
88 }
89}
90
91#[skip_serializing_none]
92#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
93pub struct SlackImageBlock {
94 pub block_id: Option<SlackBlockId>,
95 #[serde(flatten)]
96 pub image_url_or_file: SlackImageUrlOrFile,
97 pub alt_text: String,
98 pub title: Option<SlackBlockPlainTextOnly>,
99}
100
101impl From<SlackImageBlock> for SlackBlock {
102 fn from(block: SlackImageBlock) -> Self {
103 SlackBlock::Image(block)
104 }
105}
106
107#[skip_serializing_none]
108#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
109pub struct SlackActionsBlock {
110 pub block_id: Option<SlackBlockId>,
111 pub elements: Vec<SlackActionBlockElement>,
112}
113
114impl From<SlackActionsBlock> for SlackBlock {
115 fn from(block: SlackActionsBlock) -> Self {
116 SlackBlock::Actions(block)
117 }
118}
119
120#[skip_serializing_none]
121#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
122pub struct SlackContextBlock {
123 pub block_id: Option<SlackBlockId>,
124 pub elements: Vec<SlackContextBlockElement>,
125}
126
127impl From<SlackContextBlock> for SlackBlock {
128 fn from(block: SlackContextBlock) -> Self {
129 SlackBlock::Context(block)
130 }
131}
132
133#[skip_serializing_none]
134#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
135pub struct SlackInputBlock {
136 pub block_id: Option<SlackBlockId>,
137 pub label: SlackBlockPlainTextOnly,
138 pub element: SlackInputBlockElement,
139 pub hint: Option<SlackBlockPlainTextOnly>,
140 pub optional: Option<bool>,
141 pub dispatch_action: Option<bool>,
142}
143
144impl From<SlackInputBlock> for SlackBlock {
145 fn from(block: SlackInputBlock) -> Self {
146 SlackBlock::Input(block)
147 }
148}
149
150const SLACK_FILE_BLOCK_SOURCE_DEFAULT: &str = "remote";
151
152#[skip_serializing_none]
153#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
154pub struct SlackFileBlock {
155 pub block_id: Option<SlackBlockId>,
156 pub external_id: String,
157 #[default = "SLACK_FILE_BLOCK_SOURCE_DEFAULT.into()"]
158 pub source: String,
159}
160
161impl From<SlackFileBlock> for SlackBlock {
162 fn from(block: SlackFileBlock) -> Self {
163 SlackBlock::File(block)
164 }
165}
166
167#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
168#[serde(tag = "type")]
169pub enum SlackSectionBlockElement {
170 #[serde(rename = "image")]
171 Image(SlackBlockImageElement),
172 #[serde(rename = "button")]
173 Button(SlackBlockButtonElement),
174 #[serde(rename = "static_select")]
175 StaticSelect(SlackBlockStaticSelectElement),
176 #[serde(rename = "multi_static_select")]
177 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
178 #[serde(rename = "external_select")]
179 ExternalSelect(SlackBlockExternalSelectElement),
180 #[serde(rename = "multi_external_select")]
181 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
182 #[serde(rename = "users_select")]
183 UsersSelect(SlackBlockUsersSelectElement),
184 #[serde(rename = "multi_users_select")]
185 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
186 #[serde(rename = "conversations_select")]
187 ConversationsSelect(SlackBlockConversationsSelectElement),
188 #[serde(rename = "multi_conversations_select")]
189 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
190 #[serde(rename = "channels_select")]
191 ChannelsSelect(SlackBlockChannelsSelectElement),
192 #[serde(rename = "multi_channels_select")]
193 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
194 #[serde(rename = "overflow")]
195 Overflow(SlackBlockOverflowElement),
196 #[serde(rename = "datepicker")]
197 DatePicker(SlackBlockDatePickerElement),
198 #[serde(rename = "timepicker")]
199 TimePicker(SlackBlockTimePickerElement),
200 #[serde(rename = "plain_text_input")]
201 PlainTextInput(SlackBlockPlainTextInputElement),
202 #[serde(rename = "number_input")]
203 NumberInput(SlackBlockNumberInputElement),
204 #[serde(rename = "url_text_input")]
205 UrlInput(SlackBlockUrlInputElement),
206 #[serde(rename = "radio_buttons")]
207 RadioButtons(SlackBlockRadioButtonsElement),
208 #[serde(rename = "checkboxes")]
209 Checkboxes(SlackBlockCheckboxesElement),
210}
211
212#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
213#[serde(tag = "type")]
214pub enum SlackActionBlockElement {
215 #[serde(rename = "button")]
216 Button(SlackBlockButtonElement),
217 #[serde(rename = "overflow")]
218 Overflow(SlackBlockOverflowElement),
219 #[serde(rename = "datepicker")]
220 DatePicker(SlackBlockDatePickerElement),
221 #[serde(rename = "timepicker")]
222 TimePicker(SlackBlockTimePickerElement),
223 #[serde(rename = "datetimepicker")]
224 DateTimePicker(SlackBlockDateTimePickerElement),
225 #[serde(rename = "plain_text_input")]
226 PlainTextInput(SlackBlockPlainTextInputElement),
227 #[serde(rename = "number_input")]
228 NumberInput(SlackBlockNumberInputElement),
229 #[serde(rename = "url_text_input")]
230 UrlInput(SlackBlockUrlInputElement),
231 #[serde(rename = "radio_buttons")]
232 RadioButtons(SlackBlockRadioButtonsElement),
233 #[serde(rename = "checkboxes")]
234 Checkboxes(SlackBlockCheckboxesElement),
235 #[serde(rename = "static_select")]
236 StaticSelect(SlackBlockStaticSelectElement),
237 #[serde(rename = "external_select")]
238 ExternalSelect(SlackBlockExternalSelectElement),
239 #[serde(rename = "users_select")]
240 UsersSelect(SlackBlockUsersSelectElement),
241 #[serde(rename = "conversations_select")]
242 ConversationsSelect(SlackBlockConversationsSelectElement),
243 #[serde(rename = "channels_select")]
244 ChannelsSelect(SlackBlockChannelsSelectElement),
245}
246
247#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
248#[serde(tag = "type")]
249pub enum SlackContextBlockElement {
250 #[serde(rename = "image")]
251 Image(SlackBlockImageElement),
252 #[serde(rename = "plain_text")]
253 Plain(SlackBlockPlainText),
254 #[serde(rename = "mrkdwn")]
255 MarkDown(SlackBlockMarkDownText),
256}
257
258#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
259#[serde(tag = "type")]
260pub enum SlackInputBlockElement {
261 #[serde(rename = "static_select")]
262 StaticSelect(SlackBlockStaticSelectElement),
263 #[serde(rename = "multi_static_select")]
264 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
265 #[serde(rename = "external_select")]
266 ExternalSelect(SlackBlockExternalSelectElement),
267 #[serde(rename = "multi_external_select")]
268 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
269 #[serde(rename = "users_select")]
270 UsersSelect(SlackBlockUsersSelectElement),
271 #[serde(rename = "multi_users_select")]
272 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
273 #[serde(rename = "conversations_select")]
274 ConversationsSelect(SlackBlockConversationsSelectElement),
275 #[serde(rename = "multi_conversations_select")]
276 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
277 #[serde(rename = "channels_select")]
278 ChannelsSelect(SlackBlockChannelsSelectElement),
279 #[serde(rename = "multi_channels_select")]
280 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
281 #[serde(rename = "datepicker")]
282 DatePicker(SlackBlockDatePickerElement),
283 #[serde(rename = "timepicker")]
284 TimePicker(SlackBlockTimePickerElement),
285 #[serde(rename = "datetimepicker")]
286 DateTimePicker(SlackBlockDateTimePickerElement),
287 #[serde(rename = "plain_text_input")]
288 PlainTextInput(SlackBlockPlainTextInputElement),
289 #[serde(rename = "number_input")]
290 NumberInput(SlackBlockNumberInputElement),
291 #[serde(rename = "url_text_input")]
292 UrlInput(SlackBlockUrlInputElement),
293 #[serde(rename = "radio_buttons")]
294 RadioButtons(SlackBlockRadioButtonsElement),
295 #[serde(rename = "checkboxes")]
296 Checkboxes(SlackBlockCheckboxesElement),
297 #[serde(rename = "email_text_input")]
298 EmailInput(SlackBlockEmailInputElement),
299 #[serde(rename = "rich_text_input")]
300 RichTextInput(SlackBlockRichTextInputElement),
301}
302
303#[skip_serializing_none]
304#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
305pub struct SlackBlockImageElement {
306 #[serde(flatten)]
307 pub image_url_or_file: SlackImageUrlOrFile,
308 pub alt_text: String,
309}
310
311impl From<SlackBlockImageElement> for SlackSectionBlockElement {
312 fn from(element: SlackBlockImageElement) -> Self {
313 SlackSectionBlockElement::Image(element)
314 }
315}
316
317impl From<SlackBlockImageElement> for SlackContextBlockElement {
318 fn from(element: SlackBlockImageElement) -> Self {
319 SlackContextBlockElement::Image(element)
320 }
321}
322
323#[skip_serializing_none]
324#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
325pub struct SlackBlockButtonElement {
326 pub action_id: SlackActionId,
327 pub text: SlackBlockPlainTextOnly,
328 pub url: Option<Url>,
329 pub value: Option<String>,
330 pub style: Option<String>,
331 pub confirm: Option<SlackBlockConfirmItem>,
332}
333
334impl From<SlackBlockButtonElement> for SlackSectionBlockElement {
335 fn from(element: SlackBlockButtonElement) -> Self {
336 SlackSectionBlockElement::Button(element)
337 }
338}
339
340impl From<SlackBlockButtonElement> for SlackActionBlockElement {
341 fn from(element: SlackBlockButtonElement) -> Self {
342 SlackActionBlockElement::Button(element)
343 }
344}
345
346#[skip_serializing_none]
347#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
348pub struct SlackBlockConfirmItem {
349 pub title: SlackBlockPlainTextOnly,
350 pub text: SlackBlockText,
351 pub confirm: SlackBlockPlainTextOnly,
352 pub deny: SlackBlockPlainTextOnly,
353 pub style: Option<String>,
354}
355
356#[skip_serializing_none]
357#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
358pub struct SlackBlockChoiceItem<T: Into<SlackBlockText>> {
359 pub text: T,
360 pub value: String,
361 pub url: Option<Url>,
362}
363
364#[skip_serializing_none]
365#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
366pub struct SlackBlockOptionGroup<T: Into<SlackBlockText>> {
367 pub label: SlackBlockPlainTextOnly,
368 pub options: Vec<SlackBlockChoiceItem<T>>,
369}
370
371#[skip_serializing_none]
372#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
373pub struct SlackBlockStaticSelectElement {
374 pub action_id: SlackActionId,
375 pub placeholder: Option<SlackBlockPlainTextOnly>,
376 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
377 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
378 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
379 pub confirm: Option<SlackBlockConfirmItem>,
380 pub focus_on_load: Option<bool>,
381}
382
383impl From<SlackBlockStaticSelectElement> for SlackSectionBlockElement {
384 fn from(element: SlackBlockStaticSelectElement) -> Self {
385 SlackSectionBlockElement::StaticSelect(element)
386 }
387}
388
389impl From<SlackBlockStaticSelectElement> for SlackInputBlockElement {
390 fn from(element: SlackBlockStaticSelectElement) -> Self {
391 SlackInputBlockElement::StaticSelect(element)
392 }
393}
394
395impl From<SlackBlockStaticSelectElement> for SlackActionBlockElement {
396 fn from(element: SlackBlockStaticSelectElement) -> Self {
397 SlackActionBlockElement::StaticSelect(element)
398 }
399}
400
401#[skip_serializing_none]
402#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
403pub struct SlackBlockMultiStaticSelectElement {
404 pub action_id: SlackActionId,
405 pub placeholder: Option<SlackBlockPlainTextOnly>,
406 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
407 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
408 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
409 pub confirm: Option<SlackBlockConfirmItem>,
410 pub max_selected_items: Option<u64>,
411 pub focus_on_load: Option<bool>,
412}
413
414impl From<SlackBlockMultiStaticSelectElement> for SlackSectionBlockElement {
415 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
416 SlackSectionBlockElement::MultiStaticSelect(element)
417 }
418}
419
420impl From<SlackBlockMultiStaticSelectElement> for SlackInputBlockElement {
421 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
422 SlackInputBlockElement::MultiStaticSelect(element)
423 }
424}
425
426#[skip_serializing_none]
427#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
428pub struct SlackBlockExternalSelectElement {
429 pub action_id: SlackActionId,
430 pub placeholder: Option<SlackBlockPlainTextOnly>,
431 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
432 pub confirm: Option<SlackBlockConfirmItem>,
433 pub focus_on_load: Option<bool>,
434 pub min_query_length: Option<u64>,
435}
436
437impl From<SlackBlockExternalSelectElement> for SlackSectionBlockElement {
438 fn from(element: SlackBlockExternalSelectElement) -> Self {
439 SlackSectionBlockElement::ExternalSelect(element)
440 }
441}
442
443impl From<SlackBlockExternalSelectElement> for SlackInputBlockElement {
444 fn from(element: SlackBlockExternalSelectElement) -> Self {
445 SlackInputBlockElement::ExternalSelect(element)
446 }
447}
448
449impl From<SlackBlockExternalSelectElement> for SlackActionBlockElement {
450 fn from(element: SlackBlockExternalSelectElement) -> Self {
451 SlackActionBlockElement::ExternalSelect(element)
452 }
453}
454
455#[skip_serializing_none]
456#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
457pub struct SlackBlockMultiExternalSelectElement {
458 pub action_id: SlackActionId,
459 pub placeholder: Option<SlackBlockPlainTextOnly>,
460 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
461 pub confirm: Option<SlackBlockConfirmItem>,
462 pub max_selected_items: Option<u64>,
463 pub focus_on_load: Option<bool>,
464 pub min_query_length: Option<u64>,
465}
466
467impl From<SlackBlockMultiExternalSelectElement> for SlackSectionBlockElement {
468 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
469 SlackSectionBlockElement::MultiExternalSelect(element)
470 }
471}
472
473impl From<SlackBlockMultiExternalSelectElement> for SlackInputBlockElement {
474 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
475 SlackInputBlockElement::MultiExternalSelect(element)
476 }
477}
478
479#[skip_serializing_none]
480#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
481pub struct SlackBlockUsersSelectElement {
482 pub action_id: SlackActionId,
483 pub placeholder: Option<SlackBlockPlainTextOnly>,
484 pub initial_user: Option<String>,
485 pub confirm: Option<SlackBlockConfirmItem>,
486 pub focus_on_load: Option<bool>,
487}
488
489impl From<SlackBlockUsersSelectElement> for SlackSectionBlockElement {
490 fn from(element: SlackBlockUsersSelectElement) -> Self {
491 SlackSectionBlockElement::UsersSelect(element)
492 }
493}
494
495impl From<SlackBlockUsersSelectElement> for SlackInputBlockElement {
496 fn from(element: SlackBlockUsersSelectElement) -> Self {
497 SlackInputBlockElement::UsersSelect(element)
498 }
499}
500
501impl From<SlackBlockUsersSelectElement> for SlackActionBlockElement {
502 fn from(element: SlackBlockUsersSelectElement) -> Self {
503 SlackActionBlockElement::UsersSelect(element)
504 }
505}
506
507#[skip_serializing_none]
508#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
509pub struct SlackBlockMultiUsersSelectElement {
510 pub action_id: SlackActionId,
511 pub placeholder: Option<SlackBlockPlainTextOnly>,
512 pub initial_users: Option<Vec<String>>,
513 pub confirm: Option<SlackBlockConfirmItem>,
514 pub max_selected_items: Option<u64>,
515 pub focus_on_load: Option<bool>,
516}
517
518impl From<SlackBlockMultiUsersSelectElement> for SlackSectionBlockElement {
519 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
520 SlackSectionBlockElement::MultiUsersSelect(element)
521 }
522}
523
524impl From<SlackBlockMultiUsersSelectElement> for SlackInputBlockElement {
525 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
526 SlackInputBlockElement::MultiUsersSelect(element)
527 }
528}
529
530#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
531pub enum SlackConversationFilterInclude {
532 #[serde(rename = "im")]
533 Im,
534 #[serde(rename = "mpim")]
535 Mpim,
536 #[serde(rename = "public")]
537 Public,
538 #[serde(rename = "private")]
539 Private,
540}
541
542#[skip_serializing_none]
543#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
544pub struct SlackBlockConversationFilter {
545 pub include: Option<Vec<SlackConversationFilterInclude>>,
546 pub exclude_external_shared_channels: Option<bool>,
547 pub exclude_bot_users: Option<bool>,
548}
549
550#[skip_serializing_none]
551#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
552pub struct SlackBlockConversationsSelectElement {
553 pub action_id: SlackActionId,
554 pub placeholder: Option<SlackBlockPlainTextOnly>,
555 pub initial_conversation: Option<SlackConversationId>,
556 pub default_to_current_conversation: Option<bool>,
557 pub confirm: Option<SlackBlockConfirmItem>,
558 pub response_url_enabled: Option<bool>,
559 pub focus_on_load: Option<bool>,
560 pub filter: Option<SlackBlockConversationFilter>,
561}
562
563impl From<SlackBlockConversationsSelectElement> for SlackSectionBlockElement {
564 fn from(element: SlackBlockConversationsSelectElement) -> Self {
565 SlackSectionBlockElement::ConversationsSelect(element)
566 }
567}
568
569impl From<SlackBlockConversationsSelectElement> for SlackInputBlockElement {
570 fn from(element: SlackBlockConversationsSelectElement) -> Self {
571 SlackInputBlockElement::ConversationsSelect(element)
572 }
573}
574
575impl From<SlackBlockConversationsSelectElement> for SlackActionBlockElement {
576 fn from(element: SlackBlockConversationsSelectElement) -> Self {
577 SlackActionBlockElement::ConversationsSelect(element)
578 }
579}
580
581#[skip_serializing_none]
582#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
583pub struct SlackBlockMultiConversationsSelectElement {
584 pub action_id: SlackActionId,
585 pub placeholder: Option<SlackBlockPlainTextOnly>,
586 pub initial_conversations: Option<Vec<SlackConversationId>>,
587 pub default_to_current_conversation: Option<bool>,
588 pub confirm: Option<SlackBlockConfirmItem>,
589 pub max_selected_items: Option<u64>,
590 pub focus_on_load: Option<bool>,
591 pub filter: Option<SlackBlockConversationFilter>,
592}
593
594impl From<SlackBlockMultiConversationsSelectElement> for SlackSectionBlockElement {
595 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
596 SlackSectionBlockElement::MultiConversationsSelect(element)
597 }
598}
599
600impl From<SlackBlockMultiConversationsSelectElement> for SlackInputBlockElement {
601 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
602 SlackInputBlockElement::MultiConversationsSelect(element)
603 }
604}
605
606#[skip_serializing_none]
607#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
608pub struct SlackBlockChannelsSelectElement {
609 pub action_id: SlackActionId,
610 pub placeholder: Option<SlackBlockPlainTextOnly>,
611 pub initial_channel: Option<SlackChannelId>,
612 pub confirm: Option<SlackBlockConfirmItem>,
613 pub response_url_enabled: Option<bool>,
614 pub focus_on_load: Option<bool>,
615}
616
617impl From<SlackBlockChannelsSelectElement> for SlackSectionBlockElement {
618 fn from(element: SlackBlockChannelsSelectElement) -> Self {
619 SlackSectionBlockElement::ChannelsSelect(element)
620 }
621}
622
623impl From<SlackBlockChannelsSelectElement> for SlackInputBlockElement {
624 fn from(element: SlackBlockChannelsSelectElement) -> Self {
625 SlackInputBlockElement::ChannelsSelect(element)
626 }
627}
628
629impl From<SlackBlockChannelsSelectElement> for SlackActionBlockElement {
630 fn from(element: SlackBlockChannelsSelectElement) -> Self {
631 SlackActionBlockElement::ChannelsSelect(element)
632 }
633}
634
635#[skip_serializing_none]
636#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
637pub struct SlackBlockMultiChannelsSelectElement {
638 pub action_id: SlackActionId,
639 pub placeholder: Option<SlackBlockPlainTextOnly>,
640 pub initial_channels: Option<Vec<SlackChannelId>>,
641 pub confirm: Option<SlackBlockConfirmItem>,
642 pub max_selected_items: Option<u64>,
643 pub focus_on_load: Option<bool>,
644}
645
646impl From<SlackBlockMultiChannelsSelectElement> for SlackSectionBlockElement {
647 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
648 SlackSectionBlockElement::MultiChannelsSelect(element)
649 }
650}
651
652impl From<SlackBlockMultiChannelsSelectElement> for SlackInputBlockElement {
653 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
654 SlackInputBlockElement::MultiChannelsSelect(element)
655 }
656}
657
658#[skip_serializing_none]
659#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
660pub struct SlackBlockOverflowElement {
661 pub action_id: SlackActionId,
662 pub options: Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
663 pub confirm: Option<SlackBlockConfirmItem>,
664}
665
666impl From<SlackBlockOverflowElement> for SlackSectionBlockElement {
667 fn from(element: SlackBlockOverflowElement) -> Self {
668 SlackSectionBlockElement::Overflow(element)
669 }
670}
671
672impl From<SlackBlockOverflowElement> for SlackActionBlockElement {
673 fn from(element: SlackBlockOverflowElement) -> Self {
674 SlackActionBlockElement::Overflow(element)
675 }
676}
677
678#[skip_serializing_none]
679#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
680pub struct SlackBlockDatePickerElement {
681 pub action_id: SlackActionId,
682 pub placeholder: Option<SlackBlockPlainTextOnly>,
683 pub initial_date: Option<String>,
684 pub confirm: Option<SlackBlockConfirmItem>,
685 pub focus_on_load: Option<bool>,
686}
687
688impl From<SlackBlockDatePickerElement> for SlackSectionBlockElement {
689 fn from(element: SlackBlockDatePickerElement) -> Self {
690 SlackSectionBlockElement::DatePicker(element)
691 }
692}
693
694impl From<SlackBlockDatePickerElement> for SlackInputBlockElement {
695 fn from(element: SlackBlockDatePickerElement) -> Self {
696 SlackInputBlockElement::DatePicker(element)
697 }
698}
699
700impl From<SlackBlockDatePickerElement> for SlackActionBlockElement {
701 fn from(element: SlackBlockDatePickerElement) -> Self {
702 SlackActionBlockElement::DatePicker(element)
703 }
704}
705
706#[skip_serializing_none]
707#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
708pub struct SlackBlockTimePickerElement {
709 pub action_id: SlackActionId,
710 pub initial_time: Option<String>,
711 pub confirm: Option<SlackBlockConfirmItem>,
712 pub focus_on_load: Option<bool>,
713 pub placeholder: Option<SlackBlockPlainTextOnly>,
714 pub timezone: Option<String>,
715}
716
717impl From<SlackBlockTimePickerElement> for SlackSectionBlockElement {
718 fn from(element: SlackBlockTimePickerElement) -> Self {
719 SlackSectionBlockElement::TimePicker(element)
720 }
721}
722
723impl From<SlackBlockTimePickerElement> for SlackInputBlockElement {
724 fn from(element: SlackBlockTimePickerElement) -> Self {
725 SlackInputBlockElement::TimePicker(element)
726 }
727}
728
729impl From<SlackBlockTimePickerElement> for SlackActionBlockElement {
730 fn from(element: SlackBlockTimePickerElement) -> Self {
731 SlackActionBlockElement::TimePicker(element)
732 }
733}
734
735#[skip_serializing_none]
736#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
737pub struct SlackBlockDateTimePickerElement {
738 pub action_id: SlackActionId,
739 pub initial_date_time: Option<SlackDateTime>,
740 pub confirm: Option<SlackBlockConfirmItem>,
741 pub focus_on_load: Option<bool>,
742}
743
744impl From<SlackBlockDateTimePickerElement> for SlackInputBlockElement {
745 fn from(element: SlackBlockDateTimePickerElement) -> Self {
746 SlackInputBlockElement::DateTimePicker(element)
747 }
748}
749
750impl From<SlackBlockDateTimePickerElement> for SlackActionBlockElement {
751 fn from(element: SlackBlockDateTimePickerElement) -> Self {
752 SlackActionBlockElement::DateTimePicker(element)
753 }
754}
755
756#[skip_serializing_none]
757#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
758pub struct SlackBlockPlainTextInputElement {
759 pub action_id: SlackActionId,
760 pub placeholder: Option<SlackBlockPlainTextOnly>,
761 pub initial_value: Option<String>,
762 pub multiline: Option<bool>,
763 pub min_length: Option<u64>,
764 pub max_length: Option<u64>,
765 pub focus_on_load: Option<bool>,
766}
767
768impl From<SlackBlockPlainTextInputElement> for SlackSectionBlockElement {
769 fn from(element: SlackBlockPlainTextInputElement) -> Self {
770 SlackSectionBlockElement::PlainTextInput(element)
771 }
772}
773
774impl From<SlackBlockPlainTextInputElement> for SlackInputBlockElement {
775 fn from(element: SlackBlockPlainTextInputElement) -> Self {
776 SlackInputBlockElement::PlainTextInput(element)
777 }
778}
779
780impl From<SlackBlockPlainTextInputElement> for SlackActionBlockElement {
781 fn from(element: SlackBlockPlainTextInputElement) -> Self {
782 SlackActionBlockElement::PlainTextInput(element)
783 }
784}
785
786#[skip_serializing_none]
787#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
788pub struct SlackBlockNumberInputElement {
789 pub action_id: SlackActionId,
790 pub is_decimal_allowed: bool,
791 pub focus_on_load: Option<bool>,
792 pub placeholder: Option<SlackBlockPlainTextOnly>,
793 pub initial_value: Option<String>,
794 pub min_value: Option<String>,
795 pub max_value: Option<String>,
796}
797
798impl From<SlackBlockNumberInputElement> for SlackSectionBlockElement {
799 fn from(element: SlackBlockNumberInputElement) -> Self {
800 SlackSectionBlockElement::NumberInput(element)
801 }
802}
803
804impl From<SlackBlockNumberInputElement> for SlackInputBlockElement {
805 fn from(element: SlackBlockNumberInputElement) -> Self {
806 SlackInputBlockElement::NumberInput(element)
807 }
808}
809
810impl From<SlackBlockNumberInputElement> for SlackActionBlockElement {
811 fn from(element: SlackBlockNumberInputElement) -> Self {
812 SlackActionBlockElement::NumberInput(element)
813 }
814}
815
816#[skip_serializing_none]
817#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
818pub struct SlackBlockUrlInputElement {
819 pub action_id: SlackActionId,
820 pub placeholder: Option<SlackBlockPlainTextOnly>,
821 pub initial_value: Option<String>,
822}
823
824impl From<SlackBlockUrlInputElement> for SlackSectionBlockElement {
825 fn from(element: SlackBlockUrlInputElement) -> Self {
826 SlackSectionBlockElement::UrlInput(element)
827 }
828}
829
830impl From<SlackBlockUrlInputElement> for SlackInputBlockElement {
831 fn from(element: SlackBlockUrlInputElement) -> Self {
832 SlackInputBlockElement::UrlInput(element)
833 }
834}
835
836impl From<SlackBlockUrlInputElement> for SlackActionBlockElement {
837 fn from(element: SlackBlockUrlInputElement) -> Self {
838 SlackActionBlockElement::UrlInput(element)
839 }
840}
841
842#[skip_serializing_none]
843#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
844pub struct SlackBlockEmailInputElement {
845 pub action_id: SlackActionId,
846 pub focus_on_load: Option<bool>,
847 pub placeholder: Option<SlackBlockPlainTextOnly>,
848 pub initial_value: Option<EmailAddress>,
849}
850
851impl From<SlackBlockEmailInputElement> for SlackInputBlockElement {
852 fn from(element: SlackBlockEmailInputElement) -> Self {
853 SlackInputBlockElement::EmailInput(element)
854 }
855}
856
857#[skip_serializing_none]
858#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
859pub struct SlackBlockRadioButtonsElement {
860 pub action_id: SlackActionId,
861 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
862 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockText>>,
863 pub confirm: Option<SlackBlockConfirmItem>,
864 pub focus_on_load: Option<bool>,
865}
866
867impl From<SlackBlockRadioButtonsElement> for SlackSectionBlockElement {
868 fn from(element: SlackBlockRadioButtonsElement) -> Self {
869 SlackSectionBlockElement::RadioButtons(element)
870 }
871}
872
873impl From<SlackBlockRadioButtonsElement> for SlackInputBlockElement {
874 fn from(element: SlackBlockRadioButtonsElement) -> Self {
875 SlackInputBlockElement::RadioButtons(element)
876 }
877}
878
879impl From<SlackBlockRadioButtonsElement> for SlackActionBlockElement {
880 fn from(element: SlackBlockRadioButtonsElement) -> Self {
881 SlackActionBlockElement::RadioButtons(element)
882 }
883}
884
885#[skip_serializing_none]
886#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
887pub struct SlackBlockCheckboxesElement {
888 pub action_id: SlackActionId,
889 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
890 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockText>>>,
891 pub confirm: Option<SlackBlockConfirmItem>,
892 pub focus_on_load: Option<bool>,
893}
894
895impl From<SlackBlockCheckboxesElement> for SlackSectionBlockElement {
896 fn from(element: SlackBlockCheckboxesElement) -> Self {
897 SlackSectionBlockElement::Checkboxes(element)
898 }
899}
900
901impl From<SlackBlockCheckboxesElement> for SlackInputBlockElement {
902 fn from(element: SlackBlockCheckboxesElement) -> Self {
903 SlackInputBlockElement::Checkboxes(element)
904 }
905}
906
907impl From<SlackBlockCheckboxesElement> for SlackActionBlockElement {
908 fn from(element: SlackBlockCheckboxesElement) -> Self {
909 SlackActionBlockElement::Checkboxes(element)
910 }
911}
912
913#[skip_serializing_none]
917#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
918pub struct SlackBlockPlainText {
919 pub text: String,
920 pub emoji: Option<bool>,
921}
922
923#[skip_serializing_none]
927#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
928pub struct SlackBlockMarkDownText {
929 pub text: String,
930 pub verbatim: Option<bool>,
931}
932
933#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
937#[serde(tag = "type")]
938pub enum SlackBlockText {
939 #[serde(rename = "plain_text")]
940 Plain(SlackBlockPlainText),
941 #[serde(rename = "mrkdwn")]
942 MarkDown(SlackBlockMarkDownText),
943}
944
945#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
946#[serde(tag = "type", rename = "plain_text")]
947pub struct SlackBlockPlainTextOnly {
948 #[serde(flatten)]
949 value: SlackBlockPlainText,
950}
951
952impl SlackBlockPlainText {
953 pub fn as_block_text(&self) -> SlackBlockText {
954 SlackBlockText::Plain(self.clone())
955 }
956}
957
958impl From<String> for SlackBlockPlainText {
959 fn from(value: String) -> Self {
960 SlackBlockPlainText::new(value)
961 }
962}
963
964impl From<&str> for SlackBlockPlainText {
965 fn from(value: &str) -> Self {
966 SlackBlockPlainText::new(String::from(value))
967 }
968}
969
970impl SlackBlockMarkDownText {
971 pub fn as_block_text(&self) -> SlackBlockText {
972 SlackBlockText::MarkDown(self.clone())
973 }
974}
975
976impl From<String> for SlackBlockMarkDownText {
977 fn from(value: String) -> Self {
978 SlackBlockMarkDownText::new(value)
979 }
980}
981
982impl From<&str> for SlackBlockMarkDownText {
983 fn from(value: &str) -> Self {
984 SlackBlockMarkDownText::new(String::from(value))
985 }
986}
987
988impl From<SlackBlockPlainText> for SlackBlockPlainTextOnly {
989 fn from(pt: SlackBlockPlainText) -> Self {
990 SlackBlockPlainTextOnly { value: pt }
991 }
992}
993
994impl From<SlackBlockPlainText> for SlackBlockText {
995 fn from(text: SlackBlockPlainText) -> Self {
996 SlackBlockText::Plain(text)
997 }
998}
999
1000impl From<SlackBlockMarkDownText> for SlackBlockText {
1001 fn from(text: SlackBlockMarkDownText) -> Self {
1002 SlackBlockText::MarkDown(text)
1003 }
1004}
1005
1006impl From<SlackBlockPlainText> for SlackContextBlockElement {
1007 fn from(text: SlackBlockPlainText) -> Self {
1008 SlackContextBlockElement::Plain(text)
1009 }
1010}
1011
1012impl From<SlackBlockMarkDownText> for SlackContextBlockElement {
1013 fn from(text: SlackBlockMarkDownText) -> Self {
1014 SlackContextBlockElement::MarkDown(text)
1015 }
1016}
1017
1018impl From<SlackBlockPlainTextOnly> for SlackBlockText {
1019 fn from(text: SlackBlockPlainTextOnly) -> Self {
1020 SlackBlockText::Plain(text.value)
1021 }
1022}
1023
1024impl From<String> for SlackBlockPlainTextOnly {
1025 fn from(value: String) -> Self {
1026 SlackBlockPlainTextOnly {
1027 value: value.into(),
1028 }
1029 }
1030}
1031
1032impl From<&str> for SlackBlockPlainTextOnly {
1033 fn from(value: &str) -> Self {
1034 SlackBlockPlainTextOnly {
1035 value: value.into(),
1036 }
1037 }
1038}
1039
1040#[skip_serializing_none]
1044#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1045pub struct SlackVideoBlock {
1046 pub alt_text: String,
1047 pub author_name: Option<String>,
1048 pub block_id: Option<SlackBlockId>,
1049 pub description: Option<SlackBlockPlainTextOnly>,
1050 pub provider_icon_url: Option<Url>,
1051 pub provider_name: Option<String>,
1052 pub title: SlackBlockPlainTextOnly,
1053 pub title_url: Option<Url>,
1054 pub thumbnail_url: Url,
1055 pub video_url: Url,
1056}
1057
1058impl From<SlackVideoBlock> for SlackBlock {
1059 fn from(block: SlackVideoBlock) -> Self {
1060 SlackBlock::Video(block)
1061 }
1062}
1063
1064#[skip_serializing_none]
1068#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1069pub struct SlackMarkdownBlock {
1070 pub block_id: Option<SlackBlockId>,
1071 pub text: String,
1072}
1073
1074impl From<SlackMarkdownBlock> for SlackBlock {
1075 fn from(block: SlackMarkdownBlock) -> Self {
1076 SlackBlock::Markdown(block)
1077 }
1078}
1079
1080#[skip_serializing_none]
1084#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1085pub struct SlackRichTextBlock {
1086 pub block_id: Option<SlackBlockId>,
1087 pub elements: Vec<SlackRichTextElement>,
1088}
1089
1090impl From<SlackRichTextBlock> for SlackBlock {
1091 fn from(block: SlackRichTextBlock) -> Self {
1092 SlackBlock::RichText(block)
1093 }
1094}
1095
1096#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1097#[serde(tag = "type", rename_all = "snake_case")]
1098pub enum SlackRichTextInlineContent {
1099 #[serde(rename = "rich_text")]
1100 RichText(SlackRichTextBlock),
1101}
1102
1103impl From<SlackRichTextBlock> for SlackRichTextInlineContent {
1104 fn from(block: SlackRichTextBlock) -> Self {
1105 SlackRichTextInlineContent::RichText(block)
1106 }
1107}
1108
1109#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1110#[serde(tag = "type")]
1111pub enum SlackRichTextElement {
1112 #[serde(rename = "rich_text_section")]
1113 Section(SlackRichTextSection),
1114 #[serde(rename = "rich_text_list")]
1115 List(SlackRichTextList),
1116 #[serde(rename = "rich_text_preformatted")]
1117 Preformatted(SlackRichTextPreformatted),
1118 #[serde(rename = "rich_text_quote")]
1119 Quote(SlackRichTextQuote),
1120}
1121
1122impl From<SlackRichTextSection> for SlackRichTextElement {
1123 fn from(element: SlackRichTextSection) -> Self {
1124 SlackRichTextElement::Section(element)
1125 }
1126}
1127
1128impl From<SlackRichTextList> for SlackRichTextElement {
1129 fn from(list: SlackRichTextList) -> Self {
1130 SlackRichTextElement::List(list)
1131 }
1132}
1133
1134impl From<SlackRichTextPreformatted> for SlackRichTextElement {
1135 fn from(element: SlackRichTextPreformatted) -> Self {
1136 SlackRichTextElement::Preformatted(element)
1137 }
1138}
1139
1140impl From<SlackRichTextQuote> for SlackRichTextElement {
1141 fn from(element: SlackRichTextQuote) -> Self {
1142 SlackRichTextElement::Quote(element)
1143 }
1144}
1145
1146#[skip_serializing_none]
1147#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1148pub struct SlackRichTextSection {
1149 pub elements: Vec<SlackRichTextInlineElement>,
1150}
1151
1152#[skip_serializing_none]
1153#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1154pub struct SlackRichTextList {
1155 pub style: SlackRichTextListStyle,
1156 pub elements: Vec<SlackRichTextListElement>,
1157 pub indent: Option<u64>,
1158 pub offset: Option<u64>,
1159 pub border: Option<u64>,
1160}
1161
1162#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1163#[serde(tag = "type")]
1164pub enum SlackRichTextListElement {
1165 #[serde(rename = "rich_text_section")]
1166 Section(SlackRichTextSection),
1167}
1168
1169impl From<SlackRichTextSection> for SlackRichTextListElement {
1170 fn from(element: SlackRichTextSection) -> Self {
1171 SlackRichTextListElement::Section(element)
1172 }
1173}
1174
1175#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1176#[serde(rename_all = "snake_case")]
1177pub enum SlackRichTextListStyle {
1178 Bullet,
1179 Ordered,
1180}
1181
1182#[skip_serializing_none]
1183#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1184pub struct SlackRichTextPreformatted {
1185 pub elements: Vec<SlackRichTextInlineElement>,
1186 pub border: Option<u64>,
1187 pub language: Option<String>,
1188}
1189
1190#[skip_serializing_none]
1191#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1192pub struct SlackRichTextQuote {
1193 pub elements: Vec<SlackRichTextInlineElement>,
1194 pub border: Option<u64>,
1195}
1196
1197#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1198#[serde(tag = "type")]
1199pub enum SlackRichTextInlineElement {
1200 #[serde(rename = "text")]
1201 Text(SlackRichTextText),
1202 #[serde(rename = "link")]
1203 Link(SlackRichTextLink),
1204 #[serde(rename = "user")]
1205 User(SlackRichTextUser),
1206 #[serde(rename = "channel")]
1207 Channel(SlackRichTextChannel),
1208 #[serde(rename = "usergroup")]
1209 UserGroup(SlackRichTextUserGroup),
1210 #[serde(rename = "emoji")]
1211 Emoji(SlackRichTextEmoji),
1212 #[serde(rename = "date")]
1213 Date(SlackRichTextDate),
1214 #[serde(rename = "broadcast")]
1215 Broadcast(SlackRichTextBroadcast),
1216 #[serde(rename = "color")]
1217 Color(SlackRichTextColor),
1218}
1219
1220#[skip_serializing_none]
1221#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1222pub struct SlackRichTextStyle {
1223 pub bold: Option<bool>,
1224 pub italic: Option<bool>,
1225 pub strike: Option<bool>,
1226 pub code: Option<bool>,
1227 pub underline: Option<bool>,
1228 pub highlight: Option<bool>,
1229 pub client_highlight: Option<bool>,
1230 pub unlink: Option<bool>,
1231}
1232
1233#[skip_serializing_none]
1234#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1235pub struct SlackRichTextText {
1236 pub text: String,
1237 pub style: Option<SlackRichTextStyle>,
1238}
1239
1240#[skip_serializing_none]
1241#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1242pub struct SlackRichTextLink {
1243 pub url: Url,
1244 pub text: Option<String>,
1245 #[serde(rename = "unsafe")]
1246 pub unsafe_: Option<bool>,
1247 pub style: Option<SlackRichTextStyle>,
1248}
1249
1250#[skip_serializing_none]
1251#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1252pub struct SlackRichTextUser {
1253 pub user_id: SlackUserId,
1254 pub style: Option<SlackRichTextStyle>,
1255}
1256
1257#[skip_serializing_none]
1258#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1259pub struct SlackRichTextChannel {
1260 pub channel_id: SlackChannelId,
1261 pub style: Option<SlackRichTextStyle>,
1262}
1263
1264#[skip_serializing_none]
1265#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1266pub struct SlackRichTextUserGroup {
1267 pub usergroup_id: SlackUserGroupId,
1268 pub style: Option<SlackRichTextStyle>,
1269}
1270
1271#[skip_serializing_none]
1272#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1273pub struct SlackRichTextEmoji {
1274 pub name: SlackEmojiName,
1275 pub unicode: Option<String>,
1276}
1277
1278#[skip_serializing_none]
1279#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1280pub struct SlackRichTextDate {
1281 pub timestamp: SlackDateTime,
1282 pub format: String,
1283 pub fallback: Option<String>,
1284 pub style: Option<SlackRichTextStyle>,
1285}
1286
1287#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1288#[serde(rename_all = "snake_case")]
1289pub enum SlackRichTextBroadcastRange {
1290 Here,
1291 Channel,
1292 Everyone,
1293}
1294
1295#[skip_serializing_none]
1296#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1297pub struct SlackRichTextBroadcast {
1298 pub range: SlackRichTextBroadcastRange,
1299 pub style: Option<SlackRichTextStyle>,
1300}
1301
1302#[skip_serializing_none]
1303#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1304pub struct SlackRichTextColor {
1305 pub value: String,
1306}
1307
1308#[skip_serializing_none]
1312#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1313pub struct SlackBlockRichTextInputElement {
1314 pub action_id: SlackActionId,
1315 pub initial_value: Option<SlackRichTextBlock>,
1316 pub focus_on_load: Option<bool>,
1317 pub placeholder: Option<SlackBlockPlainTextOnly>,
1318}
1319
1320impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1321 fn from(element: SlackBlockRichTextInputElement) -> Self {
1322 SlackInputBlockElement::RichTextInput(element)
1323 }
1324}
1325
1326#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1327#[serde(untagged)]
1328pub enum SlackImageUrlOrFile {
1329 ImageUrl { image_url: Url },
1330 SlackFile { slack_file: SlackFileIdOrUrl },
1331}
1332
1333impl SlackImageUrlOrFile {
1334 pub fn image_url(&self) -> Option<&Url> {
1335 match self {
1336 SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1337 SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1338 SlackFileIdOrUrl::Url { url } => Some(url),
1339 _ => None,
1340 },
1341 }
1342 }
1343}
1344
1345impl From<Url> for SlackImageUrlOrFile {
1346 fn from(value: Url) -> Self {
1347 SlackImageUrlOrFile::ImageUrl { image_url: value }
1348 }
1349}
1350
1351#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1352#[serde(untagged)]
1353pub enum SlackFileIdOrUrl {
1354 Id { id: SlackFileId },
1355 Url { url: Url },
1356}
1357
1358impl From<SlackFileId> for SlackFileIdOrUrl {
1359 fn from(value: SlackFileId) -> Self {
1360 SlackFileIdOrUrl::Id { id: value }
1361 }
1362}
1363
1364#[skip_serializing_none]
1368#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1369pub struct SlackTableBlock {
1370 pub block_id: Option<SlackBlockId>,
1371 pub rows: Vec<Vec<SlackTableCell>>,
1372 pub column_settings: Option<Vec<SlackTableColumnSetting>>,
1373}
1374
1375impl From<SlackTableBlock> for SlackBlock {
1376 fn from(block: SlackTableBlock) -> Self {
1377 SlackBlock::Table(block)
1378 }
1379}
1380
1381#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1382#[serde(tag = "type")]
1383pub enum SlackTableCell {
1384 #[serde(rename = "raw_text")]
1385 RawText(SlackTableRawTextCell),
1386 #[serde(rename = "rich_text")]
1387 RichText(SlackTableRichTextCell),
1388}
1389
1390#[skip_serializing_none]
1391#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1392pub struct SlackTableRawTextCell {
1393 pub text: String,
1394}
1395
1396#[skip_serializing_none]
1397#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1398pub struct SlackTableRichTextCell {
1399 pub elements: Vec<SlackRichTextElement>,
1400}
1401
1402#[skip_serializing_none]
1403#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1404pub struct SlackTableColumnSetting {
1405 pub align: Option<SlackTableColumnAlign>,
1406 pub is_wrapped: Option<bool>,
1407}
1408
1409#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1410#[serde(rename_all = "snake_case")]
1411pub enum SlackTableColumnAlign {
1412 Left,
1413 Center,
1414 Right,
1415}
1416
1417#[skip_serializing_none]
1421#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1422pub struct SlackTaskCardBlock {
1423 pub task_id: SlackTaskId,
1424 pub title: String,
1425 pub block_id: Option<SlackBlockId>,
1426 pub status: Option<SlackTaskCardStatus>,
1427 #[serde(rename = "details")]
1428 pub details: Option<SlackRichTextInlineContent>,
1429 #[serde(rename = "output")]
1430 pub output: Option<SlackRichTextInlineContent>,
1431 pub sources: Option<Vec<SlackTaskCardSource>>,
1432}
1433
1434impl From<SlackTaskCardBlock> for SlackBlock {
1435 fn from(block: SlackTaskCardBlock) -> Self {
1436 SlackBlock::TaskCard(block)
1437 }
1438}
1439
1440#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1441#[serde(rename_all = "snake_case")]
1442pub enum SlackTaskCardStatus {
1443 Pending,
1444 InProgress,
1445 Complete,
1446 Error,
1447}
1448
1449#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1453pub struct SlackUrlSourceElement {
1454 pub url: Url,
1455 pub text: String,
1456}
1457
1458#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1459#[serde(tag = "type")]
1460pub enum SlackTaskCardSource {
1461 #[serde(rename = "url")]
1462 Url(SlackUrlSourceElement),
1463}
1464
1465impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1466 fn from(element: SlackUrlSourceElement) -> Self {
1467 SlackTaskCardSource::Url(element)
1468 }
1469}
1470
1471#[cfg(test)]
1472mod test {
1473 use super::*;
1474 use crate::blocks::SlackHomeView;
1475
1476 #[test]
1477 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1478 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1479 let block: SlackBlock = serde_json::from_str(payload)?;
1480 match block {
1481 SlackBlock::Section(section) => match section.accessory {
1482 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1483 let filter = elem.filter.expect("filter should be present");
1484 let include = filter.include.expect("include should be present");
1485 assert_eq!(include.len(), 2);
1486 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1487 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1488 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1489 assert_eq!(filter.exclude_bot_users, Some(true));
1490 }
1491 _ => panic!("Expected ConversationsSelect accessory"),
1492 },
1493 _ => panic!("Expected Section block"),
1494 }
1495 Ok(())
1496 }
1497
1498 #[test]
1499 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1500 let filter = SlackBlockConversationFilter::new()
1501 .with_include(vec![
1502 SlackConversationFilterInclude::Im,
1503 SlackConversationFilterInclude::Mpim,
1504 ])
1505 .with_exclude_bot_users(true);
1506
1507 let json = serde_json::to_value(&filter)?;
1508 assert_eq!(
1509 json,
1510 serde_json::json!({
1511 "include": ["im", "mpim"],
1512 "exclude_bot_users": true
1513 })
1514 );
1515 Ok(())
1516 }
1517
1518 #[test]
1519 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1520 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1521 .with_filter(
1522 SlackBlockConversationFilter::new()
1523 .with_include(vec![SlackConversationFilterInclude::Public])
1524 .with_exclude_external_shared_channels(true),
1525 );
1526
1527 let json = serde_json::to_string(&elem)?;
1528 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1529 assert_eq!(elem, parsed);
1530 Ok(())
1531 }
1532
1533 #[test]
1534 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1535 let elem =
1536 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1537 .with_filter(
1538 SlackBlockConversationFilter::new()
1539 .with_include(vec![
1540 SlackConversationFilterInclude::Public,
1541 SlackConversationFilterInclude::Private,
1542 ])
1543 .with_exclude_bot_users(true),
1544 );
1545
1546 let json = serde_json::to_string(&elem)?;
1547 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1548 assert_eq!(elem, parsed);
1549 Ok(())
1550 }
1551
1552 #[test]
1553 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1554 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1555
1556 let json = serde_json::to_value(&elem)?;
1557 assert!(json.get("filter").is_none());
1558 Ok(())
1559 }
1560
1561 #[test]
1562 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1563 let payload = include_str!("./fixtures/slack_image_blocks.json");
1564 let content: SlackMessageContent = serde_json::from_str(payload)?;
1565 let blocks = content.blocks.expect("Blocks should not be empty");
1566 match blocks.first() {
1567 Some(SlackBlock::Section(section)) => match §ion.accessory {
1568 Some(SlackSectionBlockElement::Image(image)) => {
1569 assert_eq!(image.alt_text, "alt text for image");
1570 match &image.image_url_or_file {
1571 SlackImageUrlOrFile::ImageUrl { image_url } => {
1572 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1573 }
1574 SlackImageUrlOrFile::SlackFile { slack_file } => {
1575 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1576 }
1577 }
1578 }
1579 _ => panic!("Expected a section block with an image accessory"),
1580 },
1581 _ => panic!("Expected a section block"),
1582 }
1583 Ok(())
1584 }
1585
1586 #[test]
1587 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1588 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1589 let block: SlackBlock = serde_json::from_str(payload)?;
1590
1591 let rich = match block {
1592 SlackBlock::RichText(r) => r,
1593 _ => panic!("Expected a RichText block"),
1594 };
1595
1596 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1597 assert_eq!(rich.elements.len(), 4);
1598
1599 let section = match &rich.elements[0] {
1601 SlackRichTextElement::Section(s) => s,
1602 _ => panic!("Expected a Section element"),
1603 };
1604 assert_eq!(section.elements.len(), 7);
1605
1606 let text = match §ion.elements[0] {
1608 SlackRichTextInlineElement::Text(t) => t,
1609 _ => panic!("Expected a Text element"),
1610 };
1611 assert_eq!(text.text, "Hello ");
1612 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
1613
1614 assert!(matches!(
1616 §ion.elements[1],
1617 SlackRichTextInlineElement::User(_)
1618 ));
1619
1620 let emoji = match §ion.elements[4] {
1622 SlackRichTextInlineElement::Emoji(e) => e,
1623 _ => panic!("Expected an Emoji element"),
1624 };
1625 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
1626
1627 let list = match &rich.elements[1] {
1629 SlackRichTextElement::List(l) => l,
1630 _ => panic!("Expected a List element"),
1631 };
1632 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
1633 assert_eq!(list.elements.len(), 2);
1634
1635 assert!(matches!(
1637 &list.elements[0],
1638 SlackRichTextListElement::Section(_)
1639 ));
1640 assert!(matches!(
1641 &list.elements[1],
1642 SlackRichTextListElement::Section(_)
1643 ));
1644
1645 assert!(matches!(
1647 &rich.elements[2],
1648 SlackRichTextElement::Preformatted(_)
1649 ));
1650
1651 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
1653
1654 Ok(())
1655 }
1656
1657 #[test]
1658 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1659 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1660 let block: SlackBlock = serde_json::from_str(payload)?;
1661 let serialized = serde_json::to_string(&block)?;
1662 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1663 assert_eq!(block, block2);
1664 Ok(())
1665 }
1666
1667 #[test]
1668 fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1669 let payload = include_str!("./fixtures/slack_table_block.json");
1670 let block: SlackBlock = serde_json::from_str(payload)?;
1671
1672 let table = match block {
1673 SlackBlock::Table(t) => t,
1674 _ => panic!("Expected a Table block"),
1675 };
1676
1677 assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
1678 assert_eq!(table.rows.len(), 2);
1679 assert_eq!(table.rows[0].len(), 2);
1680
1681 match &table.rows[0][0] {
1683 SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
1684 _ => panic!("Expected RawText cell"),
1685 }
1686
1687 match &table.rows[1][1] {
1689 SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
1690 _ => panic!("Expected RichText cell"),
1691 }
1692
1693 let settings = table
1694 .column_settings
1695 .expect("column_settings should be present");
1696 assert_eq!(settings.len(), 2);
1697 assert_eq!(settings[0].is_wrapped, Some(true));
1698 assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
1699
1700 Ok(())
1701 }
1702
1703 #[test]
1704 fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1705 let payload = include_str!("./fixtures/slack_table_block.json");
1706 let block: SlackBlock = serde_json::from_str(payload)?;
1707 let serialized = serde_json::to_string(&block)?;
1708 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1709 assert_eq!(block, block2);
1710 Ok(())
1711 }
1712
1713 #[test]
1714 fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1715 let payload = include_str!("./fixtures/slack_task_card_block.json");
1716 let block: SlackBlock = serde_json::from_str(payload)?;
1717
1718 let task_card = match block {
1719 SlackBlock::TaskCard(t) => t,
1720 _ => panic!("Expected a TaskCard block"),
1721 };
1722
1723 assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
1724 assert_eq!(task_card.title, "Fetching weather data");
1725 assert_eq!(
1726 task_card.block_id,
1727 Some(SlackBlockId("task_card_block_1".into()))
1728 );
1729 assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
1730
1731 let output = task_card.output.expect("output should be present");
1732 let output_block = match output {
1733 SlackRichTextInlineContent::RichText(b) => b,
1734 };
1735 assert_eq!(output_block.elements.len(), 1);
1736
1737 let sources = task_card.sources.expect("sources should be present");
1738 assert_eq!(sources.len(), 2);
1739 match &sources[0] {
1740 SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
1741 }
1742
1743 Ok(())
1744 }
1745
1746 #[test]
1747 fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1748 let payload = include_str!("./fixtures/slack_task_card_block.json");
1749 let block: SlackBlock = serde_json::from_str(payload)?;
1750 let serialized = serde_json::to_string(&block)?;
1751 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1752 assert_eq!(block, block2);
1753 Ok(())
1754 }
1755}