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 super::workflow::SlackBlockWorkflowButtonElement;
8use crate::*;
9
10#[skip_serializing_none]
11#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
12pub struct SlackBlockId(pub String);
13
14#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
15pub struct SlackTaskId(pub String);
16
17#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
18pub struct SlackAccessibilityLabel(pub String);
19
20#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
21#[serde(tag = "type")]
22pub enum SlackBlock {
23    #[serde(rename = "section")]
24    Section(SlackSectionBlock),
25    #[serde(rename = "header")]
26    Header(SlackHeaderBlock),
27    #[serde(rename = "divider")]
28    Divider(SlackDividerBlock),
29    #[serde(rename = "image")]
30    Image(SlackImageBlock),
31    #[serde(rename = "actions")]
32    Actions(SlackActionsBlock),
33    #[serde(rename = "context")]
34    Context(SlackContextBlock),
35    #[serde(rename = "input")]
36    Input(SlackInputBlock),
37    #[serde(rename = "file")]
38    File(SlackFileBlock),
39    #[serde(rename = "video")]
40    Video(SlackVideoBlock),
41    #[serde(rename = "markdown")]
42    Markdown(SlackMarkdownBlock),
43    #[serde(rename = "rich_text")]
44    RichText(SlackRichTextBlock),
45    #[serde(rename = "table")]
46    Table(SlackTableBlock),
47    #[serde(rename = "task_card")]
48    TaskCard(SlackTaskCardBlock),
49    #[serde(rename = "alert")]
50    Alert(SlackAlertBlock),
51    #[serde(rename = "card")]
52    Card(SlackCardBlock),
53    #[serde(rename = "carousel")]
54    Carousel(SlackCarouselBlock),
55    #[serde(rename = "context_actions")]
56    ContextActions(SlackContextActionsBlock),
57    #[serde(rename = "share_shortcut")]
58    ShareShortcut(serde_json::Value),
59    #[serde(rename = "event")]
60    Event(serde_json::Value),
61}
62
63#[skip_serializing_none]
64#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
65pub struct SlackSectionBlock {
66    pub block_id: Option<SlackBlockId>,
67    pub text: Option<SlackBlockText>,
68    pub fields: Option<Vec<SlackBlockText>>,
69    pub accessory: Option<SlackSectionBlockElement>,
70    pub expand: Option<bool>,
71}
72
73impl From<SlackSectionBlock> for SlackBlock {
74    fn from(block: SlackSectionBlock) -> Self {
75        SlackBlock::Section(block)
76    }
77}
78
79#[skip_serializing_none]
80#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
81pub struct SlackHeaderBlock {
82    pub block_id: Option<SlackBlockId>,
83    pub text: SlackBlockPlainTextOnly,
84}
85
86impl From<SlackHeaderBlock> for SlackBlock {
87    fn from(block: SlackHeaderBlock) -> Self {
88        SlackBlock::Header(block)
89    }
90}
91
92#[skip_serializing_none]
93#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
94pub struct SlackDividerBlock {
95    pub block_id: Option<SlackBlockId>,
96}
97
98impl From<SlackDividerBlock> for SlackBlock {
99    fn from(block: SlackDividerBlock) -> Self {
100        SlackBlock::Divider(block)
101    }
102}
103
104#[skip_serializing_none]
105#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
106pub struct SlackImageBlock {
107    pub block_id: Option<SlackBlockId>,
108    #[serde(flatten)]
109    pub image_url_or_file: SlackImageUrlOrFile,
110    pub alt_text: String,
111    pub title: Option<SlackBlockPlainTextOnly>,
112}
113
114impl From<SlackImageBlock> for SlackBlock {
115    fn from(block: SlackImageBlock) -> Self {
116        SlackBlock::Image(block)
117    }
118}
119
120#[skip_serializing_none]
121#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
122pub struct SlackActionsBlock {
123    pub block_id: Option<SlackBlockId>,
124    pub elements: Vec<SlackActionBlockElement>,
125}
126
127impl From<SlackActionsBlock> for SlackBlock {
128    fn from(block: SlackActionsBlock) -> Self {
129        SlackBlock::Actions(block)
130    }
131}
132
133#[skip_serializing_none]
134#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
135pub struct SlackContextBlock {
136    pub block_id: Option<SlackBlockId>,
137    pub elements: Vec<SlackContextBlockElement>,
138}
139
140impl From<SlackContextBlock> for SlackBlock {
141    fn from(block: SlackContextBlock) -> Self {
142        SlackBlock::Context(block)
143    }
144}
145
146#[skip_serializing_none]
147#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
148pub struct SlackInputBlock {
149    pub block_id: Option<SlackBlockId>,
150    pub label: SlackBlockPlainTextOnly,
151    pub element: SlackInputBlockElement,
152    pub hint: Option<SlackBlockPlainTextOnly>,
153    pub optional: Option<bool>,
154    pub dispatch_action: Option<bool>,
155}
156
157impl From<SlackInputBlock> for SlackBlock {
158    fn from(block: SlackInputBlock) -> Self {
159        SlackBlock::Input(block)
160    }
161}
162
163const SLACK_FILE_BLOCK_SOURCE_DEFAULT: &str = "remote";
164
165#[skip_serializing_none]
166#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
167pub struct SlackFileBlock {
168    pub block_id: Option<SlackBlockId>,
169    pub external_id: String,
170    #[default = "SLACK_FILE_BLOCK_SOURCE_DEFAULT.into()"]
171    pub source: String,
172}
173
174impl From<SlackFileBlock> for SlackBlock {
175    fn from(block: SlackFileBlock) -> Self {
176        SlackBlock::File(block)
177    }
178}
179
180#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
181#[serde(tag = "type")]
182pub enum SlackSectionBlockElement {
183    #[serde(rename = "image")]
184    Image(SlackBlockImageElement),
185    #[serde(rename = "button")]
186    Button(SlackBlockButtonElement),
187    #[serde(rename = "static_select")]
188    StaticSelect(SlackBlockStaticSelectElement),
189    #[serde(rename = "multi_static_select")]
190    MultiStaticSelect(SlackBlockMultiStaticSelectElement),
191    #[serde(rename = "external_select")]
192    ExternalSelect(SlackBlockExternalSelectElement),
193    #[serde(rename = "multi_external_select")]
194    MultiExternalSelect(SlackBlockMultiExternalSelectElement),
195    #[serde(rename = "users_select")]
196    UsersSelect(SlackBlockUsersSelectElement),
197    #[serde(rename = "multi_users_select")]
198    MultiUsersSelect(SlackBlockMultiUsersSelectElement),
199    #[serde(rename = "conversations_select")]
200    ConversationsSelect(SlackBlockConversationsSelectElement),
201    #[serde(rename = "multi_conversations_select")]
202    MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
203    #[serde(rename = "channels_select")]
204    ChannelsSelect(SlackBlockChannelsSelectElement),
205    #[serde(rename = "multi_channels_select")]
206    MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
207    #[serde(rename = "overflow")]
208    Overflow(SlackBlockOverflowElement),
209    #[serde(rename = "datepicker")]
210    DatePicker(SlackBlockDatePickerElement),
211    #[serde(rename = "timepicker")]
212    TimePicker(SlackBlockTimePickerElement),
213    #[serde(rename = "plain_text_input")]
214    PlainTextInput(SlackBlockPlainTextInputElement),
215    #[serde(rename = "number_input")]
216    NumberInput(SlackBlockNumberInputElement),
217    #[serde(rename = "url_text_input")]
218    UrlInput(SlackBlockUrlInputElement),
219    #[serde(rename = "radio_buttons")]
220    RadioButtons(SlackBlockRadioButtonsElement),
221    #[serde(rename = "checkboxes")]
222    Checkboxes(SlackBlockCheckboxesElement),
223    #[serde(rename = "workflow_button")]
224    WorkflowButton(SlackBlockWorkflowButtonElement),
225}
226
227#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
228#[serde(tag = "type")]
229pub enum SlackActionBlockElement {
230    #[serde(rename = "button")]
231    Button(SlackBlockButtonElement),
232    #[serde(rename = "overflow")]
233    Overflow(SlackBlockOverflowElement),
234    #[serde(rename = "datepicker")]
235    DatePicker(SlackBlockDatePickerElement),
236    #[serde(rename = "timepicker")]
237    TimePicker(SlackBlockTimePickerElement),
238    #[serde(rename = "datetimepicker")]
239    DateTimePicker(SlackBlockDateTimePickerElement),
240    #[serde(rename = "plain_text_input")]
241    PlainTextInput(SlackBlockPlainTextInputElement),
242    #[serde(rename = "number_input")]
243    NumberInput(SlackBlockNumberInputElement),
244    #[serde(rename = "url_text_input")]
245    UrlInput(SlackBlockUrlInputElement),
246    #[serde(rename = "radio_buttons")]
247    RadioButtons(SlackBlockRadioButtonsElement),
248    #[serde(rename = "checkboxes")]
249    Checkboxes(SlackBlockCheckboxesElement),
250    #[serde(rename = "static_select")]
251    StaticSelect(SlackBlockStaticSelectElement),
252    #[serde(rename = "external_select")]
253    ExternalSelect(SlackBlockExternalSelectElement),
254    #[serde(rename = "users_select")]
255    UsersSelect(SlackBlockUsersSelectElement),
256    #[serde(rename = "conversations_select")]
257    ConversationsSelect(SlackBlockConversationsSelectElement),
258    #[serde(rename = "channels_select")]
259    ChannelsSelect(SlackBlockChannelsSelectElement),
260    #[serde(rename = "workflow_button")]
261    WorkflowButton(SlackBlockWorkflowButtonElement),
262}
263
264#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
265#[serde(tag = "type")]
266pub enum SlackContextBlockElement {
267    #[serde(rename = "image")]
268    Image(SlackBlockImageElement),
269    #[serde(rename = "plain_text")]
270    Plain(SlackBlockPlainText),
271    #[serde(rename = "mrkdwn")]
272    MarkDown(SlackBlockMarkDownText),
273}
274
275#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
276#[serde(tag = "type")]
277pub enum SlackInputBlockElement {
278    #[serde(rename = "static_select")]
279    StaticSelect(SlackBlockStaticSelectElement),
280    #[serde(rename = "multi_static_select")]
281    MultiStaticSelect(SlackBlockMultiStaticSelectElement),
282    #[serde(rename = "external_select")]
283    ExternalSelect(SlackBlockExternalSelectElement),
284    #[serde(rename = "multi_external_select")]
285    MultiExternalSelect(SlackBlockMultiExternalSelectElement),
286    #[serde(rename = "users_select")]
287    UsersSelect(SlackBlockUsersSelectElement),
288    #[serde(rename = "multi_users_select")]
289    MultiUsersSelect(SlackBlockMultiUsersSelectElement),
290    #[serde(rename = "conversations_select")]
291    ConversationsSelect(SlackBlockConversationsSelectElement),
292    #[serde(rename = "multi_conversations_select")]
293    MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
294    #[serde(rename = "channels_select")]
295    ChannelsSelect(SlackBlockChannelsSelectElement),
296    #[serde(rename = "multi_channels_select")]
297    MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
298    #[serde(rename = "datepicker")]
299    DatePicker(SlackBlockDatePickerElement),
300    #[serde(rename = "timepicker")]
301    TimePicker(SlackBlockTimePickerElement),
302    #[serde(rename = "datetimepicker")]
303    DateTimePicker(SlackBlockDateTimePickerElement),
304    #[serde(rename = "plain_text_input")]
305    PlainTextInput(SlackBlockPlainTextInputElement),
306    #[serde(rename = "number_input")]
307    NumberInput(SlackBlockNumberInputElement),
308    #[serde(rename = "url_text_input")]
309    UrlInput(SlackBlockUrlInputElement),
310    #[serde(rename = "radio_buttons")]
311    RadioButtons(SlackBlockRadioButtonsElement),
312    #[serde(rename = "checkboxes")]
313    Checkboxes(SlackBlockCheckboxesElement),
314    #[serde(rename = "email_text_input")]
315    EmailInput(SlackBlockEmailInputElement),
316    #[serde(rename = "rich_text_input")]
317    RichTextInput(SlackBlockRichTextInputElement),
318    #[serde(rename = "file_input")]
319    FileInput(SlackBlockFileInputElement),
320}
321
322#[skip_serializing_none]
323#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
324pub struct SlackBlockImageElement {
325    #[serde(flatten)]
326    pub image_url_or_file: SlackImageUrlOrFile,
327    pub alt_text: String,
328}
329
330impl From<SlackBlockImageElement> for SlackSectionBlockElement {
331    fn from(element: SlackBlockImageElement) -> Self {
332        SlackSectionBlockElement::Image(element)
333    }
334}
335
336impl From<SlackBlockImageElement> for SlackContextBlockElement {
337    fn from(element: SlackBlockImageElement) -> Self {
338        SlackContextBlockElement::Image(element)
339    }
340}
341
342#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
343#[serde(rename_all = "snake_case")]
344pub enum SlackBlockButtonStyle {
345    Primary,
346    Danger,
347}
348
349#[skip_serializing_none]
350#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
351pub struct SlackBlockButtonElement {
352    pub action_id: SlackActionId,
353    pub text: SlackBlockPlainTextOnly,
354    pub url: Option<Url>,
355    pub value: Option<String>,
356    pub style: Option<SlackBlockButtonStyle>,
357    pub confirm: Option<SlackBlockConfirmItem>,
358    pub accessibility_label: Option<SlackAccessibilityLabel>,
359}
360
361impl From<SlackBlockButtonElement> for SlackSectionBlockElement {
362    fn from(element: SlackBlockButtonElement) -> Self {
363        SlackSectionBlockElement::Button(element)
364    }
365}
366
367impl From<SlackBlockButtonElement> for SlackActionBlockElement {
368    fn from(element: SlackBlockButtonElement) -> Self {
369        SlackActionBlockElement::Button(element)
370    }
371}
372
373#[skip_serializing_none]
374#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
375pub struct SlackBlockConfirmItem {
376    pub title: SlackBlockPlainTextOnly,
377    pub text: SlackBlockText,
378    pub confirm: SlackBlockPlainTextOnly,
379    pub deny: SlackBlockPlainTextOnly,
380    pub style: Option<String>,
381}
382
383#[skip_serializing_none]
384#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
385pub struct SlackBlockChoiceItem<T: Into<SlackBlockText>> {
386    pub text: T,
387    pub value: String,
388    pub description: Option<SlackBlockPlainTextOnly>,
389    pub url: Option<Url>,
390}
391
392#[skip_serializing_none]
393#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
394pub struct SlackBlockOptionGroup<T: Into<SlackBlockText>> {
395    pub label: SlackBlockPlainTextOnly,
396    pub options: Vec<SlackBlockChoiceItem<T>>,
397}
398
399#[skip_serializing_none]
400#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
401pub struct SlackBlockStaticSelectElement {
402    pub action_id: SlackActionId,
403    pub placeholder: Option<SlackBlockPlainTextOnly>,
404    pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
405    pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
406    pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
407    pub confirm: Option<SlackBlockConfirmItem>,
408    pub focus_on_load: Option<bool>,
409}
410
411impl From<SlackBlockStaticSelectElement> for SlackSectionBlockElement {
412    fn from(element: SlackBlockStaticSelectElement) -> Self {
413        SlackSectionBlockElement::StaticSelect(element)
414    }
415}
416
417impl From<SlackBlockStaticSelectElement> for SlackInputBlockElement {
418    fn from(element: SlackBlockStaticSelectElement) -> Self {
419        SlackInputBlockElement::StaticSelect(element)
420    }
421}
422
423impl From<SlackBlockStaticSelectElement> for SlackActionBlockElement {
424    fn from(element: SlackBlockStaticSelectElement) -> Self {
425        SlackActionBlockElement::StaticSelect(element)
426    }
427}
428
429#[skip_serializing_none]
430#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
431pub struct SlackBlockMultiStaticSelectElement {
432    pub action_id: SlackActionId,
433    pub placeholder: Option<SlackBlockPlainTextOnly>,
434    pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
435    pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
436    pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
437    pub confirm: Option<SlackBlockConfirmItem>,
438    pub max_selected_items: Option<u64>,
439    pub focus_on_load: Option<bool>,
440}
441
442impl From<SlackBlockMultiStaticSelectElement> for SlackSectionBlockElement {
443    fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
444        SlackSectionBlockElement::MultiStaticSelect(element)
445    }
446}
447
448impl From<SlackBlockMultiStaticSelectElement> for SlackInputBlockElement {
449    fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
450        SlackInputBlockElement::MultiStaticSelect(element)
451    }
452}
453
454#[skip_serializing_none]
455#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
456pub struct SlackBlockExternalSelectElement {
457    pub action_id: SlackActionId,
458    pub placeholder: Option<SlackBlockPlainTextOnly>,
459    pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
460    pub confirm: Option<SlackBlockConfirmItem>,
461    pub focus_on_load: Option<bool>,
462    pub min_query_length: Option<u64>,
463}
464
465impl From<SlackBlockExternalSelectElement> for SlackSectionBlockElement {
466    fn from(element: SlackBlockExternalSelectElement) -> Self {
467        SlackSectionBlockElement::ExternalSelect(element)
468    }
469}
470
471impl From<SlackBlockExternalSelectElement> for SlackInputBlockElement {
472    fn from(element: SlackBlockExternalSelectElement) -> Self {
473        SlackInputBlockElement::ExternalSelect(element)
474    }
475}
476
477impl From<SlackBlockExternalSelectElement> for SlackActionBlockElement {
478    fn from(element: SlackBlockExternalSelectElement) -> Self {
479        SlackActionBlockElement::ExternalSelect(element)
480    }
481}
482
483#[skip_serializing_none]
484#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
485pub struct SlackBlockMultiExternalSelectElement {
486    pub action_id: SlackActionId,
487    pub placeholder: Option<SlackBlockPlainTextOnly>,
488    pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
489    pub confirm: Option<SlackBlockConfirmItem>,
490    pub max_selected_items: Option<u64>,
491    pub focus_on_load: Option<bool>,
492    pub min_query_length: Option<u64>,
493}
494
495impl From<SlackBlockMultiExternalSelectElement> for SlackSectionBlockElement {
496    fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
497        SlackSectionBlockElement::MultiExternalSelect(element)
498    }
499}
500
501impl From<SlackBlockMultiExternalSelectElement> for SlackInputBlockElement {
502    fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
503        SlackInputBlockElement::MultiExternalSelect(element)
504    }
505}
506
507#[skip_serializing_none]
508#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
509pub struct SlackBlockUsersSelectElement {
510    pub action_id: SlackActionId,
511    pub placeholder: Option<SlackBlockPlainTextOnly>,
512    pub initial_user: Option<String>,
513    pub confirm: Option<SlackBlockConfirmItem>,
514    pub focus_on_load: Option<bool>,
515}
516
517impl From<SlackBlockUsersSelectElement> for SlackSectionBlockElement {
518    fn from(element: SlackBlockUsersSelectElement) -> Self {
519        SlackSectionBlockElement::UsersSelect(element)
520    }
521}
522
523impl From<SlackBlockUsersSelectElement> for SlackInputBlockElement {
524    fn from(element: SlackBlockUsersSelectElement) -> Self {
525        SlackInputBlockElement::UsersSelect(element)
526    }
527}
528
529impl From<SlackBlockUsersSelectElement> for SlackActionBlockElement {
530    fn from(element: SlackBlockUsersSelectElement) -> Self {
531        SlackActionBlockElement::UsersSelect(element)
532    }
533}
534
535#[skip_serializing_none]
536#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
537pub struct SlackBlockMultiUsersSelectElement {
538    pub action_id: SlackActionId,
539    pub placeholder: Option<SlackBlockPlainTextOnly>,
540    pub initial_users: Option<Vec<String>>,
541    pub confirm: Option<SlackBlockConfirmItem>,
542    pub max_selected_items: Option<u64>,
543    pub focus_on_load: Option<bool>,
544}
545
546impl From<SlackBlockMultiUsersSelectElement> for SlackSectionBlockElement {
547    fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
548        SlackSectionBlockElement::MultiUsersSelect(element)
549    }
550}
551
552impl From<SlackBlockMultiUsersSelectElement> for SlackInputBlockElement {
553    fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
554        SlackInputBlockElement::MultiUsersSelect(element)
555    }
556}
557
558#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
559pub enum SlackConversationFilterInclude {
560    #[serde(rename = "im")]
561    Im,
562    #[serde(rename = "mpim")]
563    Mpim,
564    #[serde(rename = "public")]
565    Public,
566    #[serde(rename = "private")]
567    Private,
568}
569
570#[skip_serializing_none]
571#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
572pub struct SlackBlockConversationFilter {
573    pub include: Option<Vec<SlackConversationFilterInclude>>,
574    pub exclude_external_shared_channels: Option<bool>,
575    pub exclude_bot_users: Option<bool>,
576}
577
578#[skip_serializing_none]
579#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
580pub struct SlackBlockConversationsSelectElement {
581    pub action_id: SlackActionId,
582    pub placeholder: Option<SlackBlockPlainTextOnly>,
583    pub initial_conversation: Option<SlackConversationId>,
584    pub default_to_current_conversation: Option<bool>,
585    pub confirm: Option<SlackBlockConfirmItem>,
586    pub response_url_enabled: Option<bool>,
587    pub focus_on_load: Option<bool>,
588    pub filter: Option<SlackBlockConversationFilter>,
589}
590
591impl From<SlackBlockConversationsSelectElement> for SlackSectionBlockElement {
592    fn from(element: SlackBlockConversationsSelectElement) -> Self {
593        SlackSectionBlockElement::ConversationsSelect(element)
594    }
595}
596
597impl From<SlackBlockConversationsSelectElement> for SlackInputBlockElement {
598    fn from(element: SlackBlockConversationsSelectElement) -> Self {
599        SlackInputBlockElement::ConversationsSelect(element)
600    }
601}
602
603impl From<SlackBlockConversationsSelectElement> for SlackActionBlockElement {
604    fn from(element: SlackBlockConversationsSelectElement) -> Self {
605        SlackActionBlockElement::ConversationsSelect(element)
606    }
607}
608
609#[skip_serializing_none]
610#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
611pub struct SlackBlockMultiConversationsSelectElement {
612    pub action_id: SlackActionId,
613    pub placeholder: Option<SlackBlockPlainTextOnly>,
614    pub initial_conversations: Option<Vec<SlackConversationId>>,
615    pub default_to_current_conversation: Option<bool>,
616    pub confirm: Option<SlackBlockConfirmItem>,
617    pub max_selected_items: Option<u64>,
618    pub focus_on_load: Option<bool>,
619    pub filter: Option<SlackBlockConversationFilter>,
620}
621
622impl From<SlackBlockMultiConversationsSelectElement> for SlackSectionBlockElement {
623    fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
624        SlackSectionBlockElement::MultiConversationsSelect(element)
625    }
626}
627
628impl From<SlackBlockMultiConversationsSelectElement> for SlackInputBlockElement {
629    fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
630        SlackInputBlockElement::MultiConversationsSelect(element)
631    }
632}
633
634#[skip_serializing_none]
635#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
636pub struct SlackBlockChannelsSelectElement {
637    pub action_id: SlackActionId,
638    pub placeholder: Option<SlackBlockPlainTextOnly>,
639    pub initial_channel: Option<SlackChannelId>,
640    pub confirm: Option<SlackBlockConfirmItem>,
641    pub response_url_enabled: Option<bool>,
642    pub focus_on_load: Option<bool>,
643}
644
645impl From<SlackBlockChannelsSelectElement> for SlackSectionBlockElement {
646    fn from(element: SlackBlockChannelsSelectElement) -> Self {
647        SlackSectionBlockElement::ChannelsSelect(element)
648    }
649}
650
651impl From<SlackBlockChannelsSelectElement> for SlackInputBlockElement {
652    fn from(element: SlackBlockChannelsSelectElement) -> Self {
653        SlackInputBlockElement::ChannelsSelect(element)
654    }
655}
656
657impl From<SlackBlockChannelsSelectElement> for SlackActionBlockElement {
658    fn from(element: SlackBlockChannelsSelectElement) -> Self {
659        SlackActionBlockElement::ChannelsSelect(element)
660    }
661}
662
663#[skip_serializing_none]
664#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
665pub struct SlackBlockMultiChannelsSelectElement {
666    pub action_id: SlackActionId,
667    pub placeholder: Option<SlackBlockPlainTextOnly>,
668    pub initial_channels: Option<Vec<SlackChannelId>>,
669    pub confirm: Option<SlackBlockConfirmItem>,
670    pub max_selected_items: Option<u64>,
671    pub focus_on_load: Option<bool>,
672}
673
674impl From<SlackBlockMultiChannelsSelectElement> for SlackSectionBlockElement {
675    fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
676        SlackSectionBlockElement::MultiChannelsSelect(element)
677    }
678}
679
680impl From<SlackBlockMultiChannelsSelectElement> for SlackInputBlockElement {
681    fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
682        SlackInputBlockElement::MultiChannelsSelect(element)
683    }
684}
685
686#[skip_serializing_none]
687#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
688pub struct SlackBlockOverflowElement {
689    pub action_id: SlackActionId,
690    pub options: Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
691    pub confirm: Option<SlackBlockConfirmItem>,
692}
693
694impl From<SlackBlockOverflowElement> for SlackSectionBlockElement {
695    fn from(element: SlackBlockOverflowElement) -> Self {
696        SlackSectionBlockElement::Overflow(element)
697    }
698}
699
700impl From<SlackBlockOverflowElement> for SlackActionBlockElement {
701    fn from(element: SlackBlockOverflowElement) -> Self {
702        SlackActionBlockElement::Overflow(element)
703    }
704}
705
706#[skip_serializing_none]
707#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
708pub struct SlackBlockDatePickerElement {
709    pub action_id: SlackActionId,
710    pub placeholder: Option<SlackBlockPlainTextOnly>,
711    pub initial_date: Option<String>,
712    pub confirm: Option<SlackBlockConfirmItem>,
713    pub focus_on_load: Option<bool>,
714}
715
716impl From<SlackBlockDatePickerElement> for SlackSectionBlockElement {
717    fn from(element: SlackBlockDatePickerElement) -> Self {
718        SlackSectionBlockElement::DatePicker(element)
719    }
720}
721
722impl From<SlackBlockDatePickerElement> for SlackInputBlockElement {
723    fn from(element: SlackBlockDatePickerElement) -> Self {
724        SlackInputBlockElement::DatePicker(element)
725    }
726}
727
728impl From<SlackBlockDatePickerElement> for SlackActionBlockElement {
729    fn from(element: SlackBlockDatePickerElement) -> Self {
730        SlackActionBlockElement::DatePicker(element)
731    }
732}
733
734#[skip_serializing_none]
735#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
736pub struct SlackBlockTimePickerElement {
737    pub action_id: SlackActionId,
738    pub initial_time: Option<String>,
739    pub confirm: Option<SlackBlockConfirmItem>,
740    pub focus_on_load: Option<bool>,
741    pub placeholder: Option<SlackBlockPlainTextOnly>,
742    pub timezone: Option<String>,
743}
744
745impl From<SlackBlockTimePickerElement> for SlackSectionBlockElement {
746    fn from(element: SlackBlockTimePickerElement) -> Self {
747        SlackSectionBlockElement::TimePicker(element)
748    }
749}
750
751impl From<SlackBlockTimePickerElement> for SlackInputBlockElement {
752    fn from(element: SlackBlockTimePickerElement) -> Self {
753        SlackInputBlockElement::TimePicker(element)
754    }
755}
756
757impl From<SlackBlockTimePickerElement> for SlackActionBlockElement {
758    fn from(element: SlackBlockTimePickerElement) -> Self {
759        SlackActionBlockElement::TimePicker(element)
760    }
761}
762
763#[skip_serializing_none]
764#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
765pub struct SlackBlockDateTimePickerElement {
766    pub action_id: SlackActionId,
767    pub initial_date_time: Option<SlackDateTime>,
768    pub confirm: Option<SlackBlockConfirmItem>,
769    pub focus_on_load: Option<bool>,
770}
771
772impl From<SlackBlockDateTimePickerElement> for SlackInputBlockElement {
773    fn from(element: SlackBlockDateTimePickerElement) -> Self {
774        SlackInputBlockElement::DateTimePicker(element)
775    }
776}
777
778impl From<SlackBlockDateTimePickerElement> for SlackActionBlockElement {
779    fn from(element: SlackBlockDateTimePickerElement) -> Self {
780        SlackActionBlockElement::DateTimePicker(element)
781    }
782}
783
784/**
785 * https://docs.slack.dev/reference/block-kit/composition-objects/dispatch-action-configuration-object
786 */
787#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
788#[serde(rename_all = "snake_case")]
789pub enum SlackDispatchActionTrigger {
790    OnEnterPressed,
791    OnCharacterEntered,
792}
793
794#[skip_serializing_none]
795#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
796pub struct SlackDispatchActionConfig {
797    pub trigger_actions_on: Option<Vec<SlackDispatchActionTrigger>>,
798}
799
800#[skip_serializing_none]
801#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
802pub struct SlackBlockPlainTextInputElement {
803    pub action_id: SlackActionId,
804    pub placeholder: Option<SlackBlockPlainTextOnly>,
805    pub initial_value: Option<String>,
806    pub multiline: Option<bool>,
807    pub min_length: Option<u64>,
808    pub max_length: Option<u64>,
809    pub focus_on_load: Option<bool>,
810    pub dispatch_action_config: Option<SlackDispatchActionConfig>,
811}
812
813impl From<SlackBlockPlainTextInputElement> for SlackSectionBlockElement {
814    fn from(element: SlackBlockPlainTextInputElement) -> Self {
815        SlackSectionBlockElement::PlainTextInput(element)
816    }
817}
818
819impl From<SlackBlockPlainTextInputElement> for SlackInputBlockElement {
820    fn from(element: SlackBlockPlainTextInputElement) -> Self {
821        SlackInputBlockElement::PlainTextInput(element)
822    }
823}
824
825impl From<SlackBlockPlainTextInputElement> for SlackActionBlockElement {
826    fn from(element: SlackBlockPlainTextInputElement) -> Self {
827        SlackActionBlockElement::PlainTextInput(element)
828    }
829}
830
831#[skip_serializing_none]
832#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
833pub struct SlackBlockNumberInputElement {
834    pub action_id: SlackActionId,
835    pub is_decimal_allowed: bool,
836    pub focus_on_load: Option<bool>,
837    pub placeholder: Option<SlackBlockPlainTextOnly>,
838    pub initial_value: Option<String>,
839    pub min_value: Option<String>,
840    pub max_value: Option<String>,
841}
842
843impl From<SlackBlockNumberInputElement> for SlackSectionBlockElement {
844    fn from(element: SlackBlockNumberInputElement) -> Self {
845        SlackSectionBlockElement::NumberInput(element)
846    }
847}
848
849impl From<SlackBlockNumberInputElement> for SlackInputBlockElement {
850    fn from(element: SlackBlockNumberInputElement) -> Self {
851        SlackInputBlockElement::NumberInput(element)
852    }
853}
854
855impl From<SlackBlockNumberInputElement> for SlackActionBlockElement {
856    fn from(element: SlackBlockNumberInputElement) -> Self {
857        SlackActionBlockElement::NumberInput(element)
858    }
859}
860
861#[skip_serializing_none]
862#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
863pub struct SlackBlockUrlInputElement {
864    pub action_id: SlackActionId,
865    pub placeholder: Option<SlackBlockPlainTextOnly>,
866    pub initial_value: Option<String>,
867}
868
869impl From<SlackBlockUrlInputElement> for SlackSectionBlockElement {
870    fn from(element: SlackBlockUrlInputElement) -> Self {
871        SlackSectionBlockElement::UrlInput(element)
872    }
873}
874
875impl From<SlackBlockUrlInputElement> for SlackInputBlockElement {
876    fn from(element: SlackBlockUrlInputElement) -> Self {
877        SlackInputBlockElement::UrlInput(element)
878    }
879}
880
881impl From<SlackBlockUrlInputElement> for SlackActionBlockElement {
882    fn from(element: SlackBlockUrlInputElement) -> Self {
883        SlackActionBlockElement::UrlInput(element)
884    }
885}
886
887#[skip_serializing_none]
888#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
889pub struct SlackBlockEmailInputElement {
890    pub action_id: SlackActionId,
891    pub focus_on_load: Option<bool>,
892    pub placeholder: Option<SlackBlockPlainTextOnly>,
893    pub initial_value: Option<EmailAddress>,
894}
895
896impl From<SlackBlockEmailInputElement> for SlackInputBlockElement {
897    fn from(element: SlackBlockEmailInputElement) -> Self {
898        SlackInputBlockElement::EmailInput(element)
899    }
900}
901
902#[skip_serializing_none]
903#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
904pub struct SlackBlockRadioButtonsElement {
905    pub action_id: SlackActionId,
906    pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
907    pub initial_option: Option<SlackBlockChoiceItem<SlackBlockText>>,
908    pub confirm: Option<SlackBlockConfirmItem>,
909    pub focus_on_load: Option<bool>,
910}
911
912impl From<SlackBlockRadioButtonsElement> for SlackSectionBlockElement {
913    fn from(element: SlackBlockRadioButtonsElement) -> Self {
914        SlackSectionBlockElement::RadioButtons(element)
915    }
916}
917
918impl From<SlackBlockRadioButtonsElement> for SlackInputBlockElement {
919    fn from(element: SlackBlockRadioButtonsElement) -> Self {
920        SlackInputBlockElement::RadioButtons(element)
921    }
922}
923
924impl From<SlackBlockRadioButtonsElement> for SlackActionBlockElement {
925    fn from(element: SlackBlockRadioButtonsElement) -> Self {
926        SlackActionBlockElement::RadioButtons(element)
927    }
928}
929
930#[skip_serializing_none]
931#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
932pub struct SlackBlockCheckboxesElement {
933    pub action_id: SlackActionId,
934    pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
935    pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockText>>>,
936    pub confirm: Option<SlackBlockConfirmItem>,
937    pub focus_on_load: Option<bool>,
938}
939
940impl From<SlackBlockCheckboxesElement> for SlackSectionBlockElement {
941    fn from(element: SlackBlockCheckboxesElement) -> Self {
942        SlackSectionBlockElement::Checkboxes(element)
943    }
944}
945
946impl From<SlackBlockCheckboxesElement> for SlackInputBlockElement {
947    fn from(element: SlackBlockCheckboxesElement) -> Self {
948        SlackInputBlockElement::Checkboxes(element)
949    }
950}
951
952impl From<SlackBlockCheckboxesElement> for SlackActionBlockElement {
953    fn from(element: SlackBlockCheckboxesElement) -> Self {
954        SlackActionBlockElement::Checkboxes(element)
955    }
956}
957
958/**
959 * 'plain_text' type of https://api.slack.com/reference/block-kit/composition-objects#text
960 */
961#[skip_serializing_none]
962#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
963pub struct SlackBlockPlainText {
964    pub text: String,
965    pub emoji: Option<bool>,
966}
967
968/**
969 * 'mrkdwn' type of https://api.slack.com/reference/block-kit/composition-objects#text
970 */
971#[skip_serializing_none]
972#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
973pub struct SlackBlockMarkDownText {
974    pub text: String,
975    pub verbatim: Option<bool>,
976}
977
978/**
979 * https://api.slack.com/reference/block-kit/composition-objects#text
980 */
981#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
982#[serde(tag = "type")]
983pub enum SlackBlockText {
984    #[serde(rename = "plain_text")]
985    Plain(SlackBlockPlainText),
986    #[serde(rename = "mrkdwn")]
987    MarkDown(SlackBlockMarkDownText),
988}
989
990#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
991#[serde(tag = "type", rename = "plain_text")]
992pub struct SlackBlockPlainTextOnly {
993    #[serde(flatten)]
994    value: SlackBlockPlainText,
995}
996
997impl SlackBlockPlainText {
998    pub fn as_block_text(&self) -> SlackBlockText {
999        SlackBlockText::Plain(self.clone())
1000    }
1001}
1002
1003impl From<String> for SlackBlockPlainText {
1004    fn from(value: String) -> Self {
1005        SlackBlockPlainText::new(value)
1006    }
1007}
1008
1009impl From<&str> for SlackBlockPlainText {
1010    fn from(value: &str) -> Self {
1011        SlackBlockPlainText::new(String::from(value))
1012    }
1013}
1014
1015impl SlackBlockMarkDownText {
1016    pub fn as_block_text(&self) -> SlackBlockText {
1017        SlackBlockText::MarkDown(self.clone())
1018    }
1019}
1020
1021impl From<String> for SlackBlockMarkDownText {
1022    fn from(value: String) -> Self {
1023        SlackBlockMarkDownText::new(value)
1024    }
1025}
1026
1027impl From<&str> for SlackBlockMarkDownText {
1028    fn from(value: &str) -> Self {
1029        SlackBlockMarkDownText::new(String::from(value))
1030    }
1031}
1032
1033impl From<SlackBlockPlainText> for SlackBlockPlainTextOnly {
1034    fn from(pt: SlackBlockPlainText) -> Self {
1035        SlackBlockPlainTextOnly { value: pt }
1036    }
1037}
1038
1039impl From<SlackBlockPlainText> for SlackBlockText {
1040    fn from(text: SlackBlockPlainText) -> Self {
1041        SlackBlockText::Plain(text)
1042    }
1043}
1044
1045impl From<SlackBlockMarkDownText> for SlackBlockText {
1046    fn from(text: SlackBlockMarkDownText) -> Self {
1047        SlackBlockText::MarkDown(text)
1048    }
1049}
1050
1051impl From<SlackBlockPlainText> for SlackContextBlockElement {
1052    fn from(text: SlackBlockPlainText) -> Self {
1053        SlackContextBlockElement::Plain(text)
1054    }
1055}
1056
1057impl From<SlackBlockMarkDownText> for SlackContextBlockElement {
1058    fn from(text: SlackBlockMarkDownText) -> Self {
1059        SlackContextBlockElement::MarkDown(text)
1060    }
1061}
1062
1063impl From<SlackBlockPlainTextOnly> for SlackBlockText {
1064    fn from(text: SlackBlockPlainTextOnly) -> Self {
1065        SlackBlockText::Plain(text.value)
1066    }
1067}
1068
1069impl From<String> for SlackBlockPlainTextOnly {
1070    fn from(value: String) -> Self {
1071        SlackBlockPlainTextOnly {
1072            value: value.into(),
1073        }
1074    }
1075}
1076
1077impl From<&str> for SlackBlockPlainTextOnly {
1078    fn from(value: &str) -> Self {
1079        SlackBlockPlainTextOnly {
1080            value: value.into(),
1081        }
1082    }
1083}
1084
1085/**
1086 * https://api.slack.com/reference/block-kit/blocks#video
1087 */
1088#[skip_serializing_none]
1089#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1090pub struct SlackVideoBlock {
1091    pub alt_text: String,
1092    pub author_name: Option<String>,
1093    pub block_id: Option<SlackBlockId>,
1094    pub description: Option<SlackBlockPlainTextOnly>,
1095    pub provider_icon_url: Option<Url>,
1096    pub provider_name: Option<String>,
1097    pub title: SlackBlockPlainTextOnly,
1098    pub title_url: Option<Url>,
1099    pub thumbnail_url: Url,
1100    pub video_url: Url,
1101}
1102
1103impl From<SlackVideoBlock> for SlackBlock {
1104    fn from(block: SlackVideoBlock) -> Self {
1105        SlackBlock::Video(block)
1106    }
1107}
1108
1109/**
1110 * https://api.slack.com/reference/block-kit/blocks#markdown
1111 */
1112#[skip_serializing_none]
1113#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1114pub struct SlackMarkdownBlock {
1115    pub block_id: Option<SlackBlockId>,
1116    pub text: String,
1117}
1118
1119impl From<SlackMarkdownBlock> for SlackBlock {
1120    fn from(block: SlackMarkdownBlock) -> Self {
1121        SlackBlock::Markdown(block)
1122    }
1123}
1124
1125/**
1126 * https://api.slack.com/reference/block-kit/blocks#rich_text
1127 */
1128#[skip_serializing_none]
1129#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1130pub struct SlackRichTextBlock {
1131    pub block_id: Option<SlackBlockId>,
1132    pub elements: Vec<SlackRichTextElement>,
1133}
1134
1135impl From<SlackRichTextBlock> for SlackBlock {
1136    fn from(block: SlackRichTextBlock) -> Self {
1137        SlackBlock::RichText(block)
1138    }
1139}
1140
1141#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1142#[serde(tag = "type", rename_all = "snake_case")]
1143pub enum SlackRichTextInlineContent {
1144    #[serde(rename = "rich_text")]
1145    RichText(SlackRichTextBlock),
1146}
1147
1148impl From<SlackRichTextBlock> for SlackRichTextInlineContent {
1149    fn from(block: SlackRichTextBlock) -> Self {
1150        SlackRichTextInlineContent::RichText(block)
1151    }
1152}
1153
1154#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1155#[serde(tag = "type")]
1156pub enum SlackRichTextElement {
1157    #[serde(rename = "rich_text_section")]
1158    Section(SlackRichTextSection),
1159    #[serde(rename = "rich_text_list")]
1160    List(SlackRichTextList),
1161    #[serde(rename = "rich_text_preformatted")]
1162    Preformatted(SlackRichTextPreformatted),
1163    #[serde(rename = "rich_text_quote")]
1164    Quote(SlackRichTextQuote),
1165}
1166
1167impl From<SlackRichTextSection> for SlackRichTextElement {
1168    fn from(element: SlackRichTextSection) -> Self {
1169        SlackRichTextElement::Section(element)
1170    }
1171}
1172
1173impl From<SlackRichTextList> for SlackRichTextElement {
1174    fn from(list: SlackRichTextList) -> Self {
1175        SlackRichTextElement::List(list)
1176    }
1177}
1178
1179impl From<SlackRichTextPreformatted> for SlackRichTextElement {
1180    fn from(element: SlackRichTextPreformatted) -> Self {
1181        SlackRichTextElement::Preformatted(element)
1182    }
1183}
1184
1185impl From<SlackRichTextQuote> for SlackRichTextElement {
1186    fn from(element: SlackRichTextQuote) -> Self {
1187        SlackRichTextElement::Quote(element)
1188    }
1189}
1190
1191#[skip_serializing_none]
1192#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1193pub struct SlackRichTextSection {
1194    pub elements: Vec<SlackRichTextInlineElement>,
1195}
1196
1197/// A bare string becomes a section holding a single unstyled text run.
1198impl From<&str> for SlackRichTextSection {
1199    fn from(value: &str) -> Self {
1200        SlackRichTextSection::new(vec![value.into()])
1201    }
1202}
1203
1204impl From<String> for SlackRichTextSection {
1205    fn from(value: String) -> Self {
1206        SlackRichTextSection::new(vec![value.into()])
1207    }
1208}
1209
1210#[skip_serializing_none]
1211#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1212pub struct SlackRichTextList {
1213    pub style: SlackRichTextListStyle,
1214    pub elements: Vec<SlackRichTextListElement>,
1215    pub indent: Option<u64>,
1216    pub offset: Option<u64>,
1217    pub border: Option<u64>,
1218}
1219
1220#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1221#[serde(tag = "type")]
1222pub enum SlackRichTextListElement {
1223    #[serde(rename = "rich_text_section")]
1224    Section(SlackRichTextSection),
1225}
1226
1227impl From<SlackRichTextSection> for SlackRichTextListElement {
1228    fn from(element: SlackRichTextSection) -> Self {
1229        SlackRichTextListElement::Section(element)
1230    }
1231}
1232
1233/// A bare string becomes a single-run section, the same as `SlackRichTextSection::from`.
1234impl From<&str> for SlackRichTextListElement {
1235    fn from(value: &str) -> Self {
1236        SlackRichTextListElement::Section(value.into())
1237    }
1238}
1239
1240impl From<String> for SlackRichTextListElement {
1241    fn from(value: String) -> Self {
1242        SlackRichTextListElement::Section(value.into())
1243    }
1244}
1245
1246#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1247#[serde(rename_all = "snake_case")]
1248pub enum SlackRichTextListStyle {
1249    Bullet,
1250    Ordered,
1251}
1252
1253#[skip_serializing_none]
1254#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1255pub struct SlackRichTextPreformatted {
1256    pub elements: Vec<SlackRichTextInlineElement>,
1257    pub border: Option<u64>,
1258    pub language: Option<String>,
1259}
1260
1261#[skip_serializing_none]
1262#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1263pub struct SlackRichTextQuote {
1264    pub elements: Vec<SlackRichTextInlineElement>,
1265    pub border: Option<u64>,
1266}
1267
1268#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1269#[serde(tag = "type")]
1270pub enum SlackRichTextInlineElement {
1271    #[serde(rename = "text")]
1272    Text(SlackRichTextText),
1273    #[serde(rename = "link")]
1274    Link(SlackRichTextLink),
1275    #[serde(rename = "user")]
1276    User(SlackRichTextUser),
1277    #[serde(rename = "channel")]
1278    Channel(SlackRichTextChannel),
1279    #[serde(rename = "usergroup")]
1280    UserGroup(SlackRichTextUserGroup),
1281    #[serde(rename = "emoji")]
1282    Emoji(SlackRichTextEmoji),
1283    #[serde(rename = "date")]
1284    Date(SlackRichTextDate),
1285    #[serde(rename = "broadcast")]
1286    Broadcast(SlackRichTextBroadcast),
1287    #[serde(rename = "color")]
1288    Color(SlackRichTextColor),
1289    #[serde(rename = "message_mention")]
1290    MessageMention(SlackRichTextMessageMention),
1291    #[serde(untagged)]
1292    Unknown(serde_json::Value),
1293}
1294
1295/// A bare string becomes an unstyled text run.
1296impl From<&str> for SlackRichTextInlineElement {
1297    fn from(value: &str) -> Self {
1298        SlackRichTextInlineElement::Text(SlackRichTextText::new(value.to_string()))
1299    }
1300}
1301
1302impl From<String> for SlackRichTextInlineElement {
1303    fn from(value: String) -> Self {
1304        SlackRichTextInlineElement::Text(SlackRichTextText::new(value))
1305    }
1306}
1307
1308impl From<SlackRichTextText> for SlackRichTextInlineElement {
1309    fn from(element: SlackRichTextText) -> Self {
1310        SlackRichTextInlineElement::Text(element)
1311    }
1312}
1313
1314impl From<SlackRichTextLink> for SlackRichTextInlineElement {
1315    fn from(element: SlackRichTextLink) -> Self {
1316        SlackRichTextInlineElement::Link(element)
1317    }
1318}
1319
1320impl From<SlackRichTextUser> for SlackRichTextInlineElement {
1321    fn from(element: SlackRichTextUser) -> Self {
1322        SlackRichTextInlineElement::User(element)
1323    }
1324}
1325
1326impl From<SlackRichTextChannel> for SlackRichTextInlineElement {
1327    fn from(element: SlackRichTextChannel) -> Self {
1328        SlackRichTextInlineElement::Channel(element)
1329    }
1330}
1331
1332impl From<SlackRichTextUserGroup> for SlackRichTextInlineElement {
1333    fn from(element: SlackRichTextUserGroup) -> Self {
1334        SlackRichTextInlineElement::UserGroup(element)
1335    }
1336}
1337
1338impl From<SlackRichTextEmoji> for SlackRichTextInlineElement {
1339    fn from(element: SlackRichTextEmoji) -> Self {
1340        SlackRichTextInlineElement::Emoji(element)
1341    }
1342}
1343
1344impl From<SlackRichTextDate> for SlackRichTextInlineElement {
1345    fn from(element: SlackRichTextDate) -> Self {
1346        SlackRichTextInlineElement::Date(element)
1347    }
1348}
1349
1350impl From<SlackRichTextBroadcast> for SlackRichTextInlineElement {
1351    fn from(element: SlackRichTextBroadcast) -> Self {
1352        SlackRichTextInlineElement::Broadcast(element)
1353    }
1354}
1355
1356impl From<SlackRichTextColor> for SlackRichTextInlineElement {
1357    fn from(element: SlackRichTextColor) -> Self {
1358        SlackRichTextInlineElement::Color(element)
1359    }
1360}
1361
1362impl From<SlackRichTextMessageMention> for SlackRichTextInlineElement {
1363    fn from(element: SlackRichTextMessageMention) -> Self {
1364        SlackRichTextInlineElement::MessageMention(element)
1365    }
1366}
1367
1368#[skip_serializing_none]
1369#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1370pub struct SlackRichTextStyle {
1371    pub bold: Option<bool>,
1372    pub italic: Option<bool>,
1373    pub strike: Option<bool>,
1374    pub code: Option<bool>,
1375    pub underline: Option<bool>,
1376    pub highlight: Option<bool>,
1377    pub client_highlight: Option<bool>,
1378    pub unlink: Option<bool>,
1379}
1380
1381#[skip_serializing_none]
1382#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1383pub struct SlackRichTextText {
1384    pub text: String,
1385    pub style: Option<SlackRichTextStyle>,
1386}
1387
1388impl SlackRichTextText {
1389    /// Sets the bold flag, preserving any other flags already set on `style`.
1390    pub fn bold(mut self) -> Self {
1391        self.style.get_or_insert_with(SlackRichTextStyle::new).bold = Some(true);
1392        self
1393    }
1394
1395    /// Sets the italic flag, preserving any other flags already set on `style`.
1396    pub fn italic(mut self) -> Self {
1397        self.style
1398            .get_or_insert_with(SlackRichTextStyle::new)
1399            .italic = Some(true);
1400        self
1401    }
1402
1403    /// Sets the strike flag, preserving any other flags already set on `style`.
1404    pub fn strike(mut self) -> Self {
1405        self.style
1406            .get_or_insert_with(SlackRichTextStyle::new)
1407            .strike = Some(true);
1408        self
1409    }
1410
1411    /// Sets the code flag, preserving any other flags already set on `style`.
1412    pub fn code(mut self) -> Self {
1413        self.style.get_or_insert_with(SlackRichTextStyle::new).code = Some(true);
1414        self
1415    }
1416}
1417
1418#[skip_serializing_none]
1419#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1420pub struct SlackRichTextLink {
1421    pub url: SlackRelaxedUrl,
1422    pub text: Option<String>,
1423    #[serde(rename = "unsafe")]
1424    pub unsafe_: Option<bool>,
1425    pub style: Option<SlackRichTextStyle>,
1426}
1427
1428#[skip_serializing_none]
1429#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1430pub struct SlackRichTextUser {
1431    pub user_id: SlackUserId,
1432    pub style: Option<SlackRichTextStyle>,
1433}
1434
1435#[skip_serializing_none]
1436#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1437pub struct SlackRichTextChannel {
1438    pub channel_id: SlackChannelId,
1439    pub style: Option<SlackRichTextStyle>,
1440}
1441
1442#[skip_serializing_none]
1443#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1444pub struct SlackRichTextUserGroup {
1445    pub usergroup_id: SlackUserGroupId,
1446    pub style: Option<SlackRichTextStyle>,
1447}
1448
1449#[skip_serializing_none]
1450#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1451pub struct SlackRichTextEmoji {
1452    pub name: SlackEmojiName,
1453    pub unicode: Option<String>,
1454}
1455
1456#[skip_serializing_none]
1457#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1458pub struct SlackRichTextDate {
1459    pub timestamp: SlackDateTime,
1460    pub format: String,
1461    pub fallback: Option<String>,
1462    pub style: Option<SlackRichTextStyle>,
1463}
1464
1465#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1466#[serde(rename_all = "snake_case")]
1467pub enum SlackRichTextBroadcastRange {
1468    Here,
1469    Channel,
1470    Everyone,
1471}
1472
1473#[skip_serializing_none]
1474#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1475pub struct SlackRichTextBroadcast {
1476    pub range: SlackRichTextBroadcastRange,
1477    pub style: Option<SlackRichTextStyle>,
1478}
1479
1480#[skip_serializing_none]
1481#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1482pub struct SlackRichTextColor {
1483    pub value: String,
1484}
1485
1486#[skip_serializing_none]
1487#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1488pub struct SlackRichTextMessageMention {
1489    pub url: SlackRelaxedUrl,
1490    pub text: Option<String>,
1491    pub channel_id: Option<SlackChannelId>,
1492    pub author_id: Option<SlackUserId>,
1493    pub message_ts: Option<SlackTs>,
1494    pub thread_ts: Option<SlackTs>,
1495    pub style: Option<SlackRichTextStyle>,
1496}
1497
1498/**
1499 * https://api.slack.com/reference/block-kit/block-elements#rich_text_input
1500 */
1501#[skip_serializing_none]
1502#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1503pub struct SlackBlockRichTextInputElement {
1504    pub action_id: SlackActionId,
1505    pub initial_value: Option<SlackRichTextBlock>,
1506    pub focus_on_load: Option<bool>,
1507    pub placeholder: Option<SlackBlockPlainTextOnly>,
1508}
1509
1510impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1511    fn from(element: SlackBlockRichTextInputElement) -> Self {
1512        SlackInputBlockElement::RichTextInput(element)
1513    }
1514}
1515
1516#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1517#[serde(untagged)]
1518pub enum SlackImageUrlOrFile {
1519    ImageUrl { image_url: Url },
1520    SlackFile { slack_file: SlackFileIdOrUrl },
1521}
1522
1523impl SlackImageUrlOrFile {
1524    pub fn image_url(&self) -> Option<&Url> {
1525        match self {
1526            SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1527            SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1528                SlackFileIdOrUrl::Url { url } => Some(url),
1529                _ => None,
1530            },
1531        }
1532    }
1533}
1534
1535impl From<Url> for SlackImageUrlOrFile {
1536    fn from(value: Url) -> Self {
1537        SlackImageUrlOrFile::ImageUrl { image_url: value }
1538    }
1539}
1540
1541#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1542#[serde(untagged)]
1543pub enum SlackFileIdOrUrl {
1544    Id { id: SlackFileId },
1545    Url { url: Url },
1546}
1547
1548impl From<SlackFileId> for SlackFileIdOrUrl {
1549    fn from(value: SlackFileId) -> Self {
1550        SlackFileIdOrUrl::Id { id: value }
1551    }
1552}
1553
1554/**
1555 * https://docs.slack.dev/reference/block-kit/blocks/table-block
1556 */
1557#[skip_serializing_none]
1558#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1559pub struct SlackTableBlock {
1560    pub block_id: Option<SlackBlockId>,
1561    pub rows: Vec<Vec<SlackTableCell>>,
1562    pub column_settings: Option<Vec<SlackTableColumnSetting>>,
1563}
1564
1565impl From<SlackTableBlock> for SlackBlock {
1566    fn from(block: SlackTableBlock) -> Self {
1567        SlackBlock::Table(block)
1568    }
1569}
1570
1571#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1572#[serde(tag = "type")]
1573pub enum SlackTableCell {
1574    #[serde(rename = "raw_text")]
1575    RawText(SlackTableRawTextCell),
1576    #[serde(rename = "rich_text")]
1577    RichText(SlackTableRichTextCell),
1578}
1579
1580/// A bare string becomes a raw-text cell.
1581impl From<&str> for SlackTableCell {
1582    fn from(value: &str) -> Self {
1583        SlackTableCell::RawText(SlackTableRawTextCell::new(value.to_string()))
1584    }
1585}
1586
1587impl From<String> for SlackTableCell {
1588    fn from(value: String) -> Self {
1589        SlackTableCell::RawText(SlackTableRawTextCell::new(value))
1590    }
1591}
1592
1593impl From<SlackTableRawTextCell> for SlackTableCell {
1594    fn from(cell: SlackTableRawTextCell) -> Self {
1595        SlackTableCell::RawText(cell)
1596    }
1597}
1598
1599impl From<SlackTableRichTextCell> for SlackTableCell {
1600    fn from(cell: SlackTableRichTextCell) -> Self {
1601        SlackTableCell::RichText(cell)
1602    }
1603}
1604
1605#[skip_serializing_none]
1606#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1607pub struct SlackTableRawTextCell {
1608    pub text: String,
1609}
1610
1611#[skip_serializing_none]
1612#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1613pub struct SlackTableRichTextCell {
1614    pub elements: Vec<SlackRichTextElement>,
1615}
1616
1617#[skip_serializing_none]
1618#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1619pub struct SlackTableColumnSetting {
1620    pub align: Option<SlackTableColumnAlign>,
1621    pub is_wrapped: Option<bool>,
1622}
1623
1624#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1625#[serde(rename_all = "snake_case")]
1626pub enum SlackTableColumnAlign {
1627    Left,
1628    Center,
1629    Right,
1630}
1631
1632/**
1633 * https://docs.slack.dev/reference/block-kit/blocks/task-card-block
1634 */
1635#[skip_serializing_none]
1636#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1637pub struct SlackTaskCardBlock {
1638    pub task_id: SlackTaskId,
1639    pub title: String,
1640    pub block_id: Option<SlackBlockId>,
1641    pub status: Option<SlackTaskCardStatus>,
1642    #[serde(rename = "details")]
1643    pub details: Option<SlackRichTextInlineContent>,
1644    #[serde(rename = "output")]
1645    pub output: Option<SlackRichTextInlineContent>,
1646    pub sources: Option<Vec<SlackTaskCardSource>>,
1647}
1648
1649impl From<SlackTaskCardBlock> for SlackBlock {
1650    fn from(block: SlackTaskCardBlock) -> Self {
1651        SlackBlock::TaskCard(block)
1652    }
1653}
1654
1655#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1656#[serde(rename_all = "snake_case")]
1657pub enum SlackTaskCardStatus {
1658    Pending,
1659    InProgress,
1660    Complete,
1661    Error,
1662}
1663
1664/**
1665 * https://docs.slack.dev/reference/block-kit/block-elements/url-source-element
1666 */
1667#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1668pub struct SlackUrlSourceElement {
1669    pub url: Url,
1670    pub text: String,
1671}
1672
1673#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1674#[serde(tag = "type")]
1675pub enum SlackTaskCardSource {
1676    #[serde(rename = "url")]
1677    Url(SlackUrlSourceElement),
1678}
1679
1680impl From<SlackUrlSourceElement> for SlackTaskCardSource {
1681    fn from(element: SlackUrlSourceElement) -> Self {
1682        SlackTaskCardSource::Url(element)
1683    }
1684}
1685
1686/**
1687 * https://docs.slack.dev/reference/block-kit/block-elements/file-input-element
1688 */
1689#[skip_serializing_none]
1690#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1691pub struct SlackBlockFileInputElement {
1692    pub action_id: SlackActionId,
1693    pub filetypes: Option<Vec<String>>,
1694    pub max_files: Option<u64>,
1695}
1696
1697impl From<SlackBlockFileInputElement> for SlackInputBlockElement {
1698    fn from(element: SlackBlockFileInputElement) -> Self {
1699        SlackInputBlockElement::FileInput(element)
1700    }
1701}
1702
1703/**
1704 * https://docs.slack.dev/reference/block-kit/blocks/alert-block
1705 */
1706#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1707#[serde(rename_all = "snake_case")]
1708pub enum SlackAlertLevel {
1709    Warning,
1710    Error,
1711    Info,
1712    Success,
1713}
1714
1715#[skip_serializing_none]
1716#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1717pub struct SlackAlertBlock {
1718    pub block_id: Option<SlackBlockId>,
1719    pub text: SlackBlockText,
1720    pub level: Option<SlackAlertLevel>,
1721}
1722
1723impl From<SlackAlertBlock> for SlackBlock {
1724    fn from(block: SlackAlertBlock) -> Self {
1725        SlackBlock::Alert(block)
1726    }
1727}
1728
1729/**
1730 * https://docs.slack.dev/reference/block-kit/blocks/card-block
1731 */
1732#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1733#[serde(tag = "type")]
1734pub enum SlackCardImageElement {
1735    #[serde(rename = "image")]
1736    Image(SlackBlockImageElement),
1737}
1738
1739impl From<SlackBlockImageElement> for SlackCardImageElement {
1740    fn from(element: SlackBlockImageElement) -> Self {
1741        SlackCardImageElement::Image(element)
1742    }
1743}
1744
1745#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1746#[serde(tag = "type")]
1747pub enum SlackCardActionBlockElement {
1748    #[serde(rename = "button")]
1749    Button(SlackBlockButtonElement),
1750}
1751
1752impl From<SlackBlockButtonElement> for SlackCardActionBlockElement {
1753    fn from(element: SlackBlockButtonElement) -> Self {
1754        SlackCardActionBlockElement::Button(element)
1755    }
1756}
1757
1758#[skip_serializing_none]
1759#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1760pub struct SlackCardBlock {
1761    pub block_id: Option<SlackBlockId>,
1762    pub title: Option<SlackBlockText>,
1763    pub subtitle: Option<SlackBlockText>,
1764    pub body: Option<SlackBlockText>,
1765    pub hero_image: Option<SlackCardImageElement>,
1766    pub icon: Option<SlackCardImageElement>,
1767    pub actions: Option<Vec<SlackCardActionBlockElement>>,
1768}
1769
1770impl From<SlackCardBlock> for SlackBlock {
1771    fn from(block: SlackCardBlock) -> Self {
1772        SlackBlock::Card(block)
1773    }
1774}
1775
1776/**
1777 * https://docs.slack.dev/reference/block-kit/blocks/carousel-block
1778 */
1779#[skip_serializing_none]
1780#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1781pub struct SlackCarouselBlock {
1782    pub block_id: Option<SlackBlockId>,
1783    pub elements: Vec<SlackBlock>,
1784}
1785
1786impl From<SlackCarouselBlock> for SlackBlock {
1787    fn from(block: SlackCarouselBlock) -> Self {
1788        SlackBlock::Carousel(block)
1789    }
1790}
1791
1792/**
1793 * https://docs.slack.dev/reference/block-kit/blocks/context-actions-block
1794 * https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element
1795 * https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element
1796 */
1797#[skip_serializing_none]
1798#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1799pub struct SlackFeedbackButtonItem {
1800    pub action_id: SlackActionId,
1801    pub value: String,
1802    pub text: SlackBlockPlainTextOnly,
1803    pub confirm: Option<SlackBlockConfirmItem>,
1804}
1805
1806#[skip_serializing_none]
1807#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1808pub struct SlackBlockFeedbackButtonsElement {
1809    pub action_id: SlackActionId,
1810    pub positive: SlackFeedbackButtonItem,
1811    pub negative: SlackFeedbackButtonItem,
1812}
1813
1814#[skip_serializing_none]
1815#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1816pub struct SlackBlockIconButtonElement {
1817    pub action_id: SlackActionId,
1818    pub icon: String,
1819    pub text: SlackBlockPlainTextOnly,
1820    pub value: Option<String>,
1821    pub confirm: Option<SlackBlockConfirmItem>,
1822    pub accessibility_label: Option<SlackAccessibilityLabel>,
1823    pub visible_to_user_ids: Option<Vec<SlackUserId>>,
1824}
1825
1826#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1827#[serde(tag = "type")]
1828pub enum SlackContextActionBlockElement {
1829    #[serde(rename = "feedback_buttons")]
1830    FeedbackButtons(SlackBlockFeedbackButtonsElement),
1831    #[serde(rename = "icon_button")]
1832    IconButton(SlackBlockIconButtonElement),
1833}
1834
1835impl From<SlackBlockFeedbackButtonsElement> for SlackContextActionBlockElement {
1836    fn from(element: SlackBlockFeedbackButtonsElement) -> Self {
1837        SlackContextActionBlockElement::FeedbackButtons(element)
1838    }
1839}
1840
1841impl From<SlackBlockIconButtonElement> for SlackContextActionBlockElement {
1842    fn from(element: SlackBlockIconButtonElement) -> Self {
1843        SlackContextActionBlockElement::IconButton(element)
1844    }
1845}
1846
1847#[skip_serializing_none]
1848#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1849pub struct SlackContextActionsBlock {
1850    pub block_id: Option<SlackBlockId>,
1851    pub elements: Vec<SlackContextActionBlockElement>,
1852}
1853
1854impl From<SlackContextActionsBlock> for SlackBlock {
1855    fn from(block: SlackContextActionsBlock) -> Self {
1856        SlackBlock::ContextActions(block)
1857    }
1858}
1859
1860#[cfg(test)]
1861mod test {
1862    use super::*;
1863    use crate::blocks::SlackHomeView;
1864
1865    #[test]
1866    fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1867        let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1868        let block: SlackBlock = serde_json::from_str(payload)?;
1869        match block {
1870            SlackBlock::Section(section) => match section.accessory {
1871                Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1872                    let filter = elem.filter.expect("filter should be present");
1873                    let include = filter.include.expect("include should be present");
1874                    assert_eq!(include.len(), 2);
1875                    assert_eq!(include[0], SlackConversationFilterInclude::Public);
1876                    assert_eq!(include[1], SlackConversationFilterInclude::Private);
1877                    assert_eq!(filter.exclude_external_shared_channels, Some(true));
1878                    assert_eq!(filter.exclude_bot_users, Some(true));
1879                }
1880                _ => panic!("Expected ConversationsSelect accessory"),
1881            },
1882            _ => panic!("Expected Section block"),
1883        }
1884        Ok(())
1885    }
1886
1887    #[test]
1888    fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1889        let filter = SlackBlockConversationFilter::new()
1890            .with_include(vec![
1891                SlackConversationFilterInclude::Im,
1892                SlackConversationFilterInclude::Mpim,
1893            ])
1894            .with_exclude_bot_users(true);
1895
1896        let json = serde_json::to_value(&filter)?;
1897        assert_eq!(
1898            json,
1899            serde_json::json!({
1900                "include": ["im", "mpim"],
1901                "exclude_bot_users": true
1902            })
1903        );
1904        Ok(())
1905    }
1906
1907    #[test]
1908    fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1909        let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1910            .with_filter(
1911                SlackBlockConversationFilter::new()
1912                    .with_include(vec![SlackConversationFilterInclude::Public])
1913                    .with_exclude_external_shared_channels(true),
1914            );
1915
1916        let json = serde_json::to_string(&elem)?;
1917        let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1918        assert_eq!(elem, parsed);
1919        Ok(())
1920    }
1921
1922    #[test]
1923    fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1924        let elem =
1925            SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1926                .with_filter(
1927                    SlackBlockConversationFilter::new()
1928                        .with_include(vec![
1929                            SlackConversationFilterInclude::Public,
1930                            SlackConversationFilterInclude::Private,
1931                        ])
1932                        .with_exclude_bot_users(true),
1933                );
1934
1935        let json = serde_json::to_string(&elem)?;
1936        let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1937        assert_eq!(elem, parsed);
1938        Ok(())
1939    }
1940
1941    #[test]
1942    fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1943        let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1944
1945        let json = serde_json::to_value(&elem)?;
1946        assert!(json.get("filter").is_none());
1947        Ok(())
1948    }
1949
1950    #[test]
1951    fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1952        let payload = include_str!("./fixtures/slack_image_blocks.json");
1953        let content: SlackMessageContent = serde_json::from_str(payload)?;
1954        let blocks = content.blocks.expect("Blocks should not be empty");
1955        match blocks.first() {
1956            Some(SlackBlock::Section(section)) => match &section.accessory {
1957                Some(SlackSectionBlockElement::Image(image)) => {
1958                    assert_eq!(image.alt_text, "alt text for image");
1959                    match &image.image_url_or_file {
1960                        SlackImageUrlOrFile::ImageUrl { image_url } => {
1961                            assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1962                        }
1963                        SlackImageUrlOrFile::SlackFile { slack_file } => {
1964                            panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1965                        }
1966                    }
1967                }
1968                _ => panic!("Expected a section block with an image accessory"),
1969            },
1970            _ => panic!("Expected a section block"),
1971        }
1972        Ok(())
1973    }
1974
1975    #[test]
1976    fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1977        let payload = include_str!("./fixtures/slack_rich_text_block.json");
1978        let block: SlackBlock = serde_json::from_str(payload)?;
1979
1980        let rich = match block {
1981            SlackBlock::RichText(r) => r,
1982            _ => panic!("Expected a RichText block"),
1983        };
1984
1985        assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1986        assert_eq!(rich.elements.len(), 4);
1987
1988        // section
1989        let section = match &rich.elements[0] {
1990            SlackRichTextElement::Section(s) => s,
1991            _ => panic!("Expected a Section element"),
1992        };
1993        assert_eq!(section.elements.len(), 7);
1994
1995        // bold text
1996        let text = match &section.elements[0] {
1997            SlackRichTextInlineElement::Text(t) => t,
1998            _ => panic!("Expected a Text element"),
1999        };
2000        assert_eq!(text.text, "Hello ");
2001        assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
2002
2003        // user
2004        assert!(matches!(
2005            &section.elements[1],
2006            SlackRichTextInlineElement::User(_)
2007        ));
2008
2009        // emoji — name should deserialize as SlackEmojiName
2010        let emoji = match &section.elements[4] {
2011            SlackRichTextInlineElement::Emoji(e) => e,
2012            _ => panic!("Expected an Emoji element"),
2013        };
2014        assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
2015
2016        // list
2017        let list = match &rich.elements[1] {
2018            SlackRichTextElement::List(l) => l,
2019            _ => panic!("Expected a List element"),
2020        };
2021        assert_eq!(list.style, SlackRichTextListStyle::Bullet);
2022        assert_eq!(list.elements.len(), 2);
2023
2024        // list items are SlackRichTextElement::Section
2025        assert!(matches!(
2026            &list.elements[0],
2027            SlackRichTextListElement::Section(_)
2028        ));
2029        assert!(matches!(
2030            &list.elements[1],
2031            SlackRichTextListElement::Section(_)
2032        ));
2033
2034        // preformatted
2035        assert!(matches!(
2036            &rich.elements[2],
2037            SlackRichTextElement::Preformatted(_)
2038        ));
2039
2040        // quote
2041        assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
2042
2043        Ok(())
2044    }
2045
2046    #[test]
2047    fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2048        let payload = include_str!("./fixtures/slack_rich_text_block.json");
2049        let block: SlackBlock = serde_json::from_str(payload)?;
2050        let serialized = serde_json::to_string(&block)?;
2051        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2052        assert_eq!(block, block2);
2053        Ok(())
2054    }
2055
2056    #[test]
2057    fn test_slack_table_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2058        let payload = include_str!("./fixtures/slack_table_block.json");
2059        let block: SlackBlock = serde_json::from_str(payload)?;
2060
2061        let table = match block {
2062            SlackBlock::Table(t) => t,
2063            _ => panic!("Expected a Table block"),
2064        };
2065
2066        assert_eq!(table.block_id, Some(SlackBlockId("table_block_1".into())));
2067        assert_eq!(table.rows.len(), 2);
2068        assert_eq!(table.rows[0].len(), 2);
2069
2070        // first row, first cell is raw_text
2071        match &table.rows[0][0] {
2072            SlackTableCell::RawText(c) => assert_eq!(c.text, "Header A"),
2073            _ => panic!("Expected RawText cell"),
2074        }
2075
2076        // second row, second cell is rich_text
2077        match &table.rows[1][1] {
2078            SlackTableCell::RichText(c) => assert_eq!(c.elements.len(), 1),
2079            _ => panic!("Expected RichText cell"),
2080        }
2081
2082        let settings = table
2083            .column_settings
2084            .expect("column_settings should be present");
2085        assert_eq!(settings.len(), 2);
2086        assert_eq!(settings[0].is_wrapped, Some(true));
2087        assert_eq!(settings[1].align, Some(SlackTableColumnAlign::Right));
2088
2089        Ok(())
2090    }
2091
2092    #[test]
2093    fn test_slack_table_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2094        let payload = include_str!("./fixtures/slack_table_block.json");
2095        let block: SlackBlock = serde_json::from_str(payload)?;
2096        let serialized = serde_json::to_string(&block)?;
2097        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2098        assert_eq!(block, block2);
2099        Ok(())
2100    }
2101
2102    #[test]
2103    fn test_slack_task_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2104        let payload = include_str!("./fixtures/slack_task_card_block.json");
2105        let block: SlackBlock = serde_json::from_str(payload)?;
2106
2107        let task_card = match block {
2108            SlackBlock::TaskCard(t) => t,
2109            _ => panic!("Expected a TaskCard block"),
2110        };
2111
2112        assert_eq!(task_card.task_id, SlackTaskId("task_1".into()));
2113        assert_eq!(task_card.title, "Fetching weather data");
2114        assert_eq!(
2115            task_card.block_id,
2116            Some(SlackBlockId("task_card_block_1".into()))
2117        );
2118        assert_eq!(task_card.status, Some(SlackTaskCardStatus::InProgress));
2119
2120        let output = task_card.output.expect("output should be present");
2121        let SlackRichTextInlineContent::RichText(output_block) = output;
2122        assert_eq!(output_block.elements.len(), 1);
2123
2124        let sources = task_card.sources.expect("sources should be present");
2125        assert_eq!(sources.len(), 2);
2126        match &sources[0] {
2127            SlackTaskCardSource::Url(u) => assert_eq!(u.text, "weather.com"),
2128        }
2129
2130        Ok(())
2131    }
2132
2133    #[test]
2134    fn test_slack_task_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2135        let payload = include_str!("./fixtures/slack_task_card_block.json");
2136        let block: SlackBlock = serde_json::from_str(payload)?;
2137        let serialized = serde_json::to_string(&block)?;
2138        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2139        assert_eq!(block, block2);
2140        Ok(())
2141    }
2142
2143    #[test]
2144    fn test_slack_alert_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2145        let payload = include_str!("./fixtures/slack_alert_block.json");
2146        let block: SlackBlock = serde_json::from_str(payload)?;
2147        match block {
2148            SlackBlock::Alert(alert) => {
2149                assert_eq!(alert.level, Some(SlackAlertLevel::Warning));
2150            }
2151            _ => panic!("Expected Alert block"),
2152        }
2153        Ok(())
2154    }
2155
2156    #[test]
2157    fn test_slack_alert_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2158        let payload = include_str!("./fixtures/slack_alert_block.json");
2159        let block: SlackBlock = serde_json::from_str(payload)?;
2160        let serialized = serde_json::to_string(&block)?;
2161        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2162        assert_eq!(block, block2);
2163        Ok(())
2164    }
2165
2166    #[test]
2167    fn test_slack_card_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2168        let payload = include_str!("./fixtures/slack_card_block.json");
2169        let block: SlackBlock = serde_json::from_str(payload)?;
2170        match block {
2171            SlackBlock::Card(card) => {
2172                assert!(card.hero_image.is_some());
2173                assert!(card.actions.is_some());
2174                let actions = card.actions.unwrap();
2175                assert_eq!(actions.len(), 1);
2176                match &actions[0] {
2177                    SlackCardActionBlockElement::Button(btn) => {
2178                        assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2179                    }
2180                }
2181            }
2182            _ => panic!("Expected Card block"),
2183        }
2184        Ok(())
2185    }
2186
2187    #[test]
2188    fn test_slack_card_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2189        let payload = include_str!("./fixtures/slack_card_block.json");
2190        let block: SlackBlock = serde_json::from_str(payload)?;
2191        let serialized = serde_json::to_string(&block)?;
2192        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2193        assert_eq!(block, block2);
2194        Ok(())
2195    }
2196
2197    #[test]
2198    fn test_slack_context_actions_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2199        let payload = include_str!("./fixtures/slack_context_actions_block.json");
2200        let block: SlackBlock = serde_json::from_str(payload)?;
2201        match block {
2202            SlackBlock::ContextActions(ctx) => {
2203                assert_eq!(ctx.elements.len(), 1);
2204                match &ctx.elements[0] {
2205                    SlackContextActionBlockElement::IconButton(btn) => {
2206                        assert_eq!(btn.icon, "trash");
2207                    }
2208                    _ => panic!("Expected IconButton element"),
2209                }
2210            }
2211            _ => panic!("Expected ContextActions block"),
2212        }
2213        Ok(())
2214    }
2215
2216    #[test]
2217    fn test_slack_context_actions_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2218        let payload = include_str!("./fixtures/slack_context_actions_block.json");
2219        let block: SlackBlock = serde_json::from_str(payload)?;
2220        let serialized = serde_json::to_string(&block)?;
2221        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2222        assert_eq!(block, block2);
2223        Ok(())
2224    }
2225
2226    #[test]
2227    fn test_slack_workflow_button_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2228        let payload = include_str!("./fixtures/slack_workflow_button.json");
2229        let block: SlackBlock = serde_json::from_str(payload)?;
2230        match block {
2231            SlackBlock::Actions(actions) => {
2232                assert_eq!(actions.elements.len(), 1);
2233                match &actions.elements[0] {
2234                    SlackActionBlockElement::WorkflowButton(btn) => {
2235                        assert_eq!(btn.style, Some(SlackBlockButtonStyle::Primary));
2236                        let params = btn
2237                            .workflow
2238                            .trigger
2239                            .customizable_input_parameters
2240                            .as_ref()
2241                            .expect("params should be present");
2242                        assert_eq!(params.len(), 1);
2243                        assert_eq!(params[0].name, "user_input");
2244                    }
2245                    _ => panic!("Expected WorkflowButton element"),
2246                }
2247            }
2248            _ => panic!("Expected Actions block"),
2249        }
2250        Ok(())
2251    }
2252
2253    #[test]
2254    fn test_slack_workflow_button_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
2255        let payload = include_str!("./fixtures/slack_workflow_button.json");
2256        let block: SlackBlock = serde_json::from_str(payload)?;
2257        let serialized = serde_json::to_string(&block)?;
2258        let block2: SlackBlock = serde_json::from_str(&serialized)?;
2259        assert_eq!(block, block2);
2260        Ok(())
2261    }
2262
2263    #[test]
2264    fn test_rich_text_message_mention_deserialize() -> Result<(), Box<dyn std::error::Error>> {
2265        let payload = serde_json::json!({
2266            "type": "rich_text",
2267            "block_id": "msgm1",
2268            "elements": [
2269                {
2270                    "type": "rich_text_section",
2271                    "elements": [
2272                        {
2273                            "type": "message_mention",
2274                            "url": "https://acme.slack.com/archives/C12345678/p1784153496441789?thread_ts=1784153496.441789&cid=C12345678",
2275                            "text": "a message",
2276                            "channel_id": "C12345678",
2277                            "author_id": "U12345678",
2278                            "message_ts": "1784153496.441789",
2279                            "thread_ts": "1784153496.441789"
2280                        }
2281                    ]
2282                }
2283            ]
2284        })
2285        .to_string();
2286        let block: SlackBlock = serde_json::from_str(&payload)?;
2287        match block {
2288            SlackBlock::RichText(rich_text) => match &rich_text.elements[0] {
2289                SlackRichTextElement::Section(section) => match &section.elements[0] {
2290                    SlackRichTextInlineElement::MessageMention(mention) => {
2291                        assert_eq!(mention.channel_id, Some(SlackChannelId("C12345678".into())));
2292                        assert_eq!(mention.author_id, Some(SlackUserId("U12345678".into())));
2293                        assert_eq!(
2294                            mention.message_ts,
2295                            Some(SlackTs("1784153496.441789".into()))
2296                        );
2297                    }
2298                    other => panic!("Expected MessageMention element, got {other:?}"),
2299                },
2300                _ => panic!("Expected Section element"),
2301            },
2302            _ => panic!("Expected RichText block"),
2303        }
2304        Ok(())
2305    }
2306
2307    #[test]
2308    fn test_rich_text_unknown_inline_element_deserialize() -> Result<(), Box<dyn std::error::Error>>
2309    {
2310        let payload = serde_json::json!({
2311            "type": "rich_text_section",
2312            "elements": [
2313                {
2314                    "type": "some_future_element",
2315                    "foo": "bar"
2316                }
2317            ]
2318        })
2319        .to_string();
2320        let section: SlackRichTextElement = serde_json::from_str(&payload)?;
2321        match section {
2322            SlackRichTextElement::Section(section) => match &section.elements[0] {
2323                SlackRichTextInlineElement::Unknown(value) => {
2324                    assert_eq!(value["type"], "some_future_element");
2325                }
2326                other => panic!("Expected Unknown element, got {other:?}"),
2327            },
2328            _ => panic!("Expected Section element"),
2329        }
2330        Ok(())
2331    }
2332
2333    #[test]
2334    fn rich_text_str_converts_to_text_inline_element() -> Result<(), Box<dyn std::error::Error>> {
2335        let element: SlackRichTextInlineElement = "hi".into();
2336        assert_eq!(
2337            serde_json::to_value(&element)?,
2338            serde_json::json!({"type": "text", "text": "hi"})
2339        );
2340
2341        let owned: SlackRichTextInlineElement = "hi".to_string().into();
2342        assert_eq!(
2343            serde_json::to_value(&owned)?,
2344            serde_json::to_value(&element)?
2345        );
2346        Ok(())
2347    }
2348
2349    #[test]
2350    fn rich_text_leaf_elements_convert_to_inline_elements() -> Result<(), Box<dyn std::error::Error>>
2351    {
2352        let text: SlackRichTextInlineElement = SlackRichTextText::new("t".to_string()).into();
2353        assert_eq!(
2354            serde_json::to_value(&text)?,
2355            serde_json::json!({"type": "text", "text": "t"})
2356        );
2357
2358        let link: SlackRichTextInlineElement =
2359            SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into())).into();
2360        assert_eq!(
2361            serde_json::to_value(&link)?,
2362            serde_json::json!({"type": "link", "url": "https://example.com"})
2363        );
2364
2365        let user: SlackRichTextInlineElement =
2366            SlackRichTextUser::new(SlackUserId("U1".into())).into();
2367        assert_eq!(
2368            serde_json::to_value(&user)?,
2369            serde_json::json!({"type": "user", "user_id": "U1"})
2370        );
2371
2372        let channel: SlackRichTextInlineElement =
2373            SlackRichTextChannel::new(SlackChannelId("C1".into())).into();
2374        assert_eq!(
2375            serde_json::to_value(&channel)?,
2376            serde_json::json!({"type": "channel", "channel_id": "C1"})
2377        );
2378
2379        let usergroup: SlackRichTextInlineElement =
2380            SlackRichTextUserGroup::new(SlackUserGroupId("G1".into())).into();
2381        assert_eq!(
2382            serde_json::to_value(&usergroup)?,
2383            serde_json::json!({"type": "usergroup", "usergroup_id": "G1"})
2384        );
2385
2386        let emoji: SlackRichTextInlineElement =
2387            SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into();
2388        assert_eq!(
2389            serde_json::to_value(&emoji)?,
2390            serde_json::json!({"type": "emoji", "name": "wave"})
2391        );
2392
2393        let date: SlackRichTextInlineElement = SlackRichTextDate::new(
2394            SlackDateTime("2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>()?),
2395            "{date_short}".to_string(),
2396        )
2397        .into();
2398        assert_eq!(
2399            serde_json::to_value(&date)?,
2400            serde_json::json!({"type": "date", "timestamp": 1_577_839_362_i64, "format": "{date_short}"})
2401        );
2402
2403        let broadcast: SlackRichTextInlineElement =
2404            SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into();
2405        assert_eq!(
2406            serde_json::to_value(&broadcast)?,
2407            serde_json::json!({"type": "broadcast", "range": "here"})
2408        );
2409
2410        let color: SlackRichTextInlineElement =
2411            SlackRichTextColor::new("#ff0000".to_string()).into();
2412        assert_eq!(
2413            serde_json::to_value(&color)?,
2414            serde_json::json!({"type": "color", "value": "#ff0000"})
2415        );
2416
2417        let mention: SlackRichTextInlineElement = SlackRichTextMessageMention::new(
2418            SlackRelaxedUrl("https://example.com/archives/C1/p1".into()),
2419        )
2420        .into();
2421        assert_eq!(
2422            serde_json::to_value(&mention)?,
2423            serde_json::json!({"type": "message_mention", "url": "https://example.com/archives/C1/p1"})
2424        );
2425
2426        Ok(())
2427    }
2428
2429    #[test]
2430    fn rich_text_list_accepts_str_items() -> Result<(), Box<dyn std::error::Error>> {
2431        let payload = include_str!("./fixtures/slack_rich_text_block.json");
2432        let block: SlackBlock = serde_json::from_str(payload)?;
2433        let expected_list = match block {
2434            SlackBlock::RichText(r) => match &r.elements[1] {
2435                SlackRichTextElement::List(l) => l.clone(),
2436                other => panic!("Expected List element, got {other:?}"),
2437            },
2438            _ => panic!("Expected RichText block"),
2439        };
2440
2441        let list = SlackRichTextList::new(
2442            SlackRichTextListStyle::Bullet,
2443            vec!["Item one".into(), "Item two".into()],
2444        )
2445        .with_indent(0);
2446
2447        assert_eq!(list, expected_list);
2448        Ok(())
2449    }
2450
2451    #[test]
2452    fn rich_text_style_helpers_merge_flags() -> Result<(), Box<dyn std::error::Error>> {
2453        let bold = SlackRichTextText::new("hi".to_string()).bold();
2454        let json = serde_json::to_value(&bold)?;
2455        assert_eq!(json["style"], serde_json::json!({"bold": true}));
2456
2457        let both = SlackRichTextText::new("hi".to_string())
2458            .with_style(SlackRichTextStyle::new().with_italic(true))
2459            .bold();
2460        let json2 = serde_json::to_value(&both)?;
2461        assert_eq!(
2462            json2["style"],
2463            serde_json::json!({"bold": true, "italic": true})
2464        );
2465
2466        let struck = SlackRichTextText::new("hi".to_string()).strike();
2467        assert_eq!(
2468            serde_json::to_value(&struck)?["style"],
2469            serde_json::json!({"strike": true})
2470        );
2471
2472        let coded = SlackRichTextText::new("hi".to_string()).code();
2473        assert_eq!(
2474            serde_json::to_value(&coded)?["style"],
2475            serde_json::json!({"code": true})
2476        );
2477
2478        Ok(())
2479    }
2480
2481    #[test]
2482    fn table_block_builds_from_str_cells() -> Result<(), Box<dyn std::error::Error>> {
2483        let payload = include_str!("./fixtures/slack_table_block.json");
2484        let expected: serde_json::Value = serde_json::from_str(payload)?;
2485
2486        let block: SlackBlock = SlackTableBlock::new(vec![
2487            vec!["Header A".into(), "Header B".into()],
2488            vec![
2489                "Data 1A".into(),
2490                SlackTableRichTextCell::new(vec![SlackRichTextSection::new(vec![
2491                    SlackRichTextLink::new(SlackRelaxedUrl("https://slack.com".into()))
2492                        .with_text("Data 1B".to_string())
2493                        .into(),
2494                ])
2495                .into()])
2496                .into(),
2497            ],
2498        ])
2499        .with_block_id(SlackBlockId("table_block_1".into()))
2500        .with_column_settings(vec![
2501            SlackTableColumnSetting::new().with_is_wrapped(true),
2502            SlackTableColumnSetting::new().with_align(SlackTableColumnAlign::Right),
2503        ])
2504        .into();
2505
2506        assert_eq!(serde_json::to_value(&block)?, expected);
2507        Ok(())
2508    }
2509
2510    #[test]
2511    fn rich_text_block_builds_fixture_with_conversions() -> Result<(), Box<dyn std::error::Error>> {
2512        let payload = include_str!("./fixtures/slack_rich_text_block.json");
2513        let expected: serde_json::Value = serde_json::from_str(payload)?;
2514
2515        let section: SlackRichTextElement = SlackRichTextSection::new(vec![
2516            SlackRichTextText::new("Hello ".to_string()).bold().into(),
2517            SlackRichTextUser::new(SlackUserId("U123ABC456".into())).into(),
2518            "! Check out ".into(),
2519            SlackRichTextLink::new(SlackRelaxedUrl("https://example.com".into()))
2520                .with_text("this link".to_string())
2521                .with_style(SlackRichTextStyle::new().with_italic(true))
2522                .into(),
2523            SlackRichTextEmoji::new(SlackEmojiName("wave".into())).into(),
2524            SlackRichTextChannel::new(SlackChannelId("C123ABC456".into())).into(),
2525            SlackRichTextBroadcast::new(SlackRichTextBroadcastRange::Here).into(),
2526        ])
2527        .into();
2528
2529        let list: SlackRichTextElement = SlackRichTextList::new(
2530            SlackRichTextListStyle::Bullet,
2531            vec!["Item one".into(), "Item two".into()],
2532        )
2533        .with_indent(0)
2534        .into();
2535
2536        let preformatted: SlackRichTextElement =
2537            SlackRichTextPreformatted::new(vec![SlackRichTextText::new(
2538                "fn main() {}\n".to_string(),
2539            )
2540            .into()])
2541            .with_border(1)
2542            .into();
2543
2544        let quote: SlackRichTextElement =
2545            SlackRichTextQuote::new(vec![SlackRichTextText::new("A wise quote".to_string())
2546                .italic()
2547                .into()])
2548            .into();
2549
2550        let block: SlackBlock = SlackRichTextBlock::new(vec![section, list, preformatted, quote])
2551            .with_block_id(SlackBlockId("test_block".into()))
2552            .into();
2553
2554        assert_eq!(serde_json::to_value(&block)?, expected);
2555        Ok(())
2556    }
2557}