Skip to main content

slack_morphism/models/blocks/
kit.rs

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/**
914 * 'plain_text' type of https://api.slack.com/reference/block-kit/composition-objects#text
915 */
916#[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/**
924 * 'mrkdwn' type of https://api.slack.com/reference/block-kit/composition-objects#text
925 */
926#[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/**
934 * https://api.slack.com/reference/block-kit/composition-objects#text
935 */
936#[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/**
1041 * https://api.slack.com/reference/block-kit/blocks#video
1042 */
1043#[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/**
1065 * https://api.slack.com/reference/block-kit/blocks#markdown
1066 */
1067#[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/**
1081 * https://api.slack.com/reference/block-kit/blocks#rich_text
1082 */
1083#[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/**
1309 * https://api.slack.com/reference/block-kit/block-elements#rich_text_input
1310 */
1311#[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/**
1365 * https://docs.slack.dev/reference/block-kit/blocks/table-block
1366 */
1367#[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/**
1418 * https://docs.slack.dev/reference/block-kit/blocks/task-card-block
1419 */
1420#[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/**
1450 * https://docs.slack.dev/reference/block-kit/block-elements/url-source-element
1451 */
1452#[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 &section.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        // section
1600        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        // bold text
1607        let text = match &section.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        // user
1615        assert!(matches!(
1616            &section.elements[1],
1617            SlackRichTextInlineElement::User(_)
1618        ));
1619
1620        // emoji — name should deserialize as SlackEmojiName
1621        let emoji = match &section.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        // list
1628        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        // list items are SlackRichTextElement::Section
1636        assert!(matches!(
1637            &list.elements[0],
1638            SlackRichTextListElement::Section(_)
1639        ));
1640        assert!(matches!(
1641            &list.elements[1],
1642            SlackRichTextListElement::Section(_)
1643        ));
1644
1645        // preformatted
1646        assert!(matches!(
1647            &rich.elements[2],
1648            SlackRichTextElement::Preformatted(_)
1649        ));
1650
1651        // quote
1652        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        // first row, first cell is raw_text
1682        match &table.rows[0][0] {
1683            SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
1684            _ => panic!("Expected RawText cell"),
1685        }
1686
1687        // second row, second cell is rich_text
1688        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}