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