1use serde::{Deserialize, Serialize};
2
3use super::chart::DataVisualizationBlock;
4use super::composition::{SlackFile, SlackIcon, TextObject};
5use super::element::{
6 ActionsElement, ButtonElement, ContextActionsElement, ContextElement, ImageElement,
7 InputElement, SectionAccessory, UrlSourceElement,
8};
9use super::rich_text::RichTextBlock;
10use super::table::{DataTableBlock, TableBlock};
11use super::tag::{tagged_enum, Tag, Tagged};
12
13tagged_enum! {
14 pub enum Block("type") {
16 Actions(ActionsBlock),
17 Alert(AlertBlock),
18 Card(CardBlock),
19 Carousel(CarouselBlock),
20 Container(ContainerBlock),
21 Context(ContextBlock),
22 ContextActions(ContextActionsBlock),
23 DataTable(DataTableBlock),
24 DataVisualization(DataVisualizationBlock),
25 Divider(DividerBlock),
26 File(FileBlock),
27 Header(HeaderBlock),
28 Image(ImageBlock),
29 Input(InputBlock),
30 Markdown(MarkdownBlock),
31 Plan(PlanBlock),
32 RichText(RichTextBlock),
33 Section(SectionBlock),
34 Table(TableBlock),
35 TaskCard(TaskCardBlock),
36 Video(VideoBlock),
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct ActionsBlock {
43 r#type: Tag<Self>,
44 pub elements: Vec<ActionsElement>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub block_id: Option<String>,
47}
48
49impl Tagged for ActionsBlock {
50 const TAG: &'static str = "actions";
51}
52
53impl ActionsBlock {
54 pub fn new(elements: Vec<ActionsElement>) -> Self {
55 Self {
56 r#type: Tag::default(),
57 elements,
58 block_id: None,
59 }
60 }
61
62 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
63 self.block_id = Some(block_id.into());
64 self
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct AlertBlock {
71 r#type: Tag<Self>,
72 pub text: TextObject,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub level: Option<AlertLevel>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub block_id: Option<String>,
77}
78
79impl Tagged for AlertBlock {
80 const TAG: &'static str = "alert";
81}
82
83impl AlertBlock {
84 pub fn new(text: impl Into<TextObject>) -> Self {
85 Self {
86 r#type: Tag::default(),
87 text: text.into(),
88 level: None,
89 block_id: None,
90 }
91 }
92
93 pub fn level(mut self, level: AlertLevel) -> Self {
94 self.level = Some(level);
95 self
96 }
97
98 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
99 self.block_id = Some(block_id.into());
100 self
101 }
102}
103
104#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
106pub struct CardBlock {
107 r#type: Tag<Self>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub block_id: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub hero_image: Option<ImageElement>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub icon: Option<ImageElement>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub title: Option<TextObject>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub subtitle: Option<TextObject>,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub body: Option<TextObject>,
120 #[serde(default, skip_serializing_if = "Vec::is_empty")]
121 pub actions: Vec<ButtonElement>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub slack_icon: Option<SlackIcon>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub subtext: Option<TextObject>,
126}
127
128impl Tagged for CardBlock {
129 const TAG: &'static str = "card";
130}
131
132impl CardBlock {
133 pub fn new() -> Self {
134 Self::default()
135 }
136
137 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
138 self.block_id = Some(block_id.into());
139 self
140 }
141
142 pub fn hero_image(mut self, hero_image: ImageElement) -> Self {
143 self.hero_image = Some(hero_image);
144 self
145 }
146
147 pub fn icon(mut self, icon: ImageElement) -> Self {
148 self.icon = Some(icon);
149 self
150 }
151
152 pub fn title(mut self, title: impl Into<TextObject>) -> Self {
153 self.title = Some(title.into());
154 self
155 }
156
157 pub fn subtitle(mut self, subtitle: impl Into<TextObject>) -> Self {
158 self.subtitle = Some(subtitle.into());
159 self
160 }
161
162 pub fn body(mut self, body: impl Into<TextObject>) -> Self {
163 self.body = Some(body.into());
164 self
165 }
166
167 pub fn actions(mut self, actions: Vec<ButtonElement>) -> Self {
168 self.actions = actions;
169 self
170 }
171
172 pub fn slack_icon(mut self, slack_icon: SlackIcon) -> Self {
173 self.slack_icon = Some(slack_icon);
174 self
175 }
176
177 pub fn subtext(mut self, subtext: impl Into<TextObject>) -> Self {
178 self.subtext = Some(subtext.into());
179 self
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub struct CarouselBlock {
186 r#type: Tag<Self>,
187 pub elements: Vec<CardBlock>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub block_id: Option<String>,
190}
191
192impl Tagged for CarouselBlock {
193 const TAG: &'static str = "carousel";
194}
195
196impl CarouselBlock {
197 pub fn new(elements: Vec<CardBlock>) -> Self {
198 Self {
199 r#type: Tag::default(),
200 elements,
201 block_id: None,
202 }
203 }
204
205 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
206 self.block_id = Some(block_id.into());
207 self
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
213pub struct ContainerBlock {
214 r#type: Tag<Self>,
215 pub title: TextObject,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub rich_text_title: Option<RichTextBlock>,
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub subtitle: Option<TextObject>,
220 pub child_blocks: Vec<Block>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub block_id: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub width: Option<ContainerWidth>,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub icon: Option<ImageElement>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub is_collapsible: Option<bool>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub default_collapsed: Option<bool>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub has_header_divider: Option<bool>,
233}
234
235impl Tagged for ContainerBlock {
236 const TAG: &'static str = "container";
237}
238
239impl ContainerBlock {
240 pub fn new(title: impl Into<TextObject>, child_blocks: Vec<Block>) -> Self {
241 Self {
242 r#type: Tag::default(),
243 title: title.into(),
244 rich_text_title: None,
245 subtitle: None,
246 child_blocks,
247 block_id: None,
248 width: None,
249 icon: None,
250 is_collapsible: None,
251 default_collapsed: None,
252 has_header_divider: None,
253 }
254 }
255
256 pub fn rich_text_title(mut self, rich_text_title: RichTextBlock) -> Self {
257 self.rich_text_title = Some(rich_text_title);
258 self
259 }
260
261 pub fn subtitle(mut self, subtitle: impl Into<TextObject>) -> Self {
262 self.subtitle = Some(subtitle.into());
263 self
264 }
265
266 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
267 self.block_id = Some(block_id.into());
268 self
269 }
270
271 pub fn width(mut self, width: ContainerWidth) -> Self {
272 self.width = Some(width);
273 self
274 }
275
276 pub fn icon(mut self, icon: ImageElement) -> Self {
277 self.icon = Some(icon);
278 self
279 }
280
281 pub fn is_collapsible(mut self, is_collapsible: bool) -> Self {
282 self.is_collapsible = Some(is_collapsible);
283 self
284 }
285
286 pub fn default_collapsed(mut self, default_collapsed: bool) -> Self {
287 self.default_collapsed = Some(default_collapsed);
288 self
289 }
290
291 pub fn has_header_divider(mut self, has_header_divider: bool) -> Self {
292 self.has_header_divider = Some(has_header_divider);
293 self
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
299pub struct ContextBlock {
300 r#type: Tag<Self>,
301 pub elements: Vec<ContextElement>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub block_id: Option<String>,
304}
305
306impl Tagged for ContextBlock {
307 const TAG: &'static str = "context";
308}
309
310impl ContextBlock {
311 pub fn new(elements: Vec<ContextElement>) -> Self {
312 Self {
313 r#type: Tag::default(),
314 elements,
315 block_id: None,
316 }
317 }
318
319 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
320 self.block_id = Some(block_id.into());
321 self
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327pub struct ContextActionsBlock {
328 r#type: Tag<Self>,
329 pub elements: Vec<ContextActionsElement>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub block_id: Option<String>,
332}
333
334impl Tagged for ContextActionsBlock {
335 const TAG: &'static str = "context_actions";
336}
337
338impl ContextActionsBlock {
339 pub fn new(elements: Vec<ContextActionsElement>) -> Self {
340 Self {
341 r#type: Tag::default(),
342 elements,
343 block_id: None,
344 }
345 }
346
347 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
348 self.block_id = Some(block_id.into());
349 self
350 }
351}
352
353#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
355pub struct DividerBlock {
356 r#type: Tag<Self>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub block_id: Option<String>,
359}
360
361impl Tagged for DividerBlock {
362 const TAG: &'static str = "divider";
363}
364
365impl DividerBlock {
366 pub fn new() -> Self {
367 Self::default()
368 }
369
370 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
371 self.block_id = Some(block_id.into());
372 self
373 }
374}
375
376#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
378pub struct FileBlock {
379 r#type: Tag<Self>,
380 pub external_id: String,
381 pub source: String,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub block_id: Option<String>,
384}
385
386impl Tagged for FileBlock {
387 const TAG: &'static str = "file";
388}
389
390impl FileBlock {
391 pub fn new(external_id: impl Into<String>, source: impl Into<String>) -> Self {
392 Self {
393 r#type: Tag::default(),
394 external_id: external_id.into(),
395 source: source.into(),
396 block_id: None,
397 }
398 }
399
400 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
401 self.block_id = Some(block_id.into());
402 self
403 }
404}
405
406#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
408pub struct HeaderBlock {
409 r#type: Tag<Self>,
410 pub text: TextObject,
411 #[serde(skip_serializing_if = "Option::is_none")]
412 pub block_id: Option<String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub level: Option<u8>,
415}
416
417impl Tagged for HeaderBlock {
418 const TAG: &'static str = "header";
419}
420
421impl HeaderBlock {
422 pub fn new(text: impl Into<TextObject>) -> Self {
423 Self {
424 r#type: Tag::default(),
425 text: text.into(),
426 block_id: None,
427 level: None,
428 }
429 }
430
431 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
432 self.block_id = Some(block_id.into());
433 self
434 }
435
436 pub fn level(mut self, level: u8) -> Self {
437 self.level = Some(level);
438 self
439 }
440}
441
442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
444pub struct ImageBlock {
445 r#type: Tag<Self>,
446 pub alt_text: String,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub image_url: Option<String>,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub slack_file: Option<SlackFile>,
451 #[serde(skip_serializing_if = "Option::is_none")]
452 pub title: Option<TextObject>,
453 #[serde(skip_serializing_if = "Option::is_none")]
454 pub block_id: Option<String>,
455}
456
457impl Tagged for ImageBlock {
458 const TAG: &'static str = "image";
459}
460
461impl ImageBlock {
462 pub fn new(image_url: impl Into<String>, alt_text: impl Into<String>) -> Self {
463 Self {
464 r#type: Tag::default(),
465 alt_text: alt_text.into(),
466 image_url: Some(image_url.into()),
467 slack_file: None,
468 title: None,
469 block_id: None,
470 }
471 }
472
473 pub fn from_slack_file(slack_file: SlackFile, alt_text: impl Into<String>) -> Self {
474 Self {
475 r#type: Tag::default(),
476 alt_text: alt_text.into(),
477 image_url: None,
478 slack_file: Some(slack_file),
479 title: None,
480 block_id: None,
481 }
482 }
483
484 pub fn image_url(mut self, image_url: impl Into<String>) -> Self {
485 self.image_url = Some(image_url.into());
486 self
487 }
488
489 pub fn slack_file(mut self, slack_file: SlackFile) -> Self {
490 self.slack_file = Some(slack_file);
491 self
492 }
493
494 pub fn title(mut self, title: impl Into<TextObject>) -> Self {
495 self.title = Some(title.into());
496 self
497 }
498
499 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
500 self.block_id = Some(block_id.into());
501 self
502 }
503}
504
505#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
507pub struct InputBlock {
508 r#type: Tag<Self>,
509 pub label: TextObject,
510 pub element: InputElement,
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub dispatch_action: Option<bool>,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub block_id: Option<String>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub hint: Option<TextObject>,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub optional: Option<bool>,
519}
520
521impl Tagged for InputBlock {
522 const TAG: &'static str = "input";
523}
524
525impl InputBlock {
526 pub fn new(label: impl Into<TextObject>, element: impl Into<InputElement>) -> Self {
527 Self {
528 r#type: Tag::default(),
529 label: label.into(),
530 element: element.into(),
531 dispatch_action: None,
532 block_id: None,
533 hint: None,
534 optional: None,
535 }
536 }
537
538 pub fn dispatch_action(mut self, dispatch_action: bool) -> Self {
539 self.dispatch_action = Some(dispatch_action);
540 self
541 }
542
543 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
544 self.block_id = Some(block_id.into());
545 self
546 }
547
548 pub fn hint(mut self, hint: impl Into<TextObject>) -> Self {
549 self.hint = Some(hint.into());
550 self
551 }
552
553 pub fn optional(mut self, optional: bool) -> Self {
554 self.optional = Some(optional);
555 self
556 }
557}
558
559#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
561pub struct MarkdownBlock {
562 r#type: Tag<Self>,
563 pub text: String,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 pub block_id: Option<String>,
566}
567
568impl Tagged for MarkdownBlock {
569 const TAG: &'static str = "markdown";
570}
571
572impl MarkdownBlock {
573 pub fn new(text: impl Into<String>) -> Self {
574 Self {
575 r#type: Tag::default(),
576 text: text.into(),
577 block_id: None,
578 }
579 }
580
581 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
582 self.block_id = Some(block_id.into());
583 self
584 }
585}
586
587#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
589pub struct PlanBlock {
590 r#type: Tag<Self>,
591 pub title: String,
592 pub tasks: Vec<PlanTask>,
593 #[serde(skip_serializing_if = "Option::is_none")]
594 pub block_id: Option<String>,
595}
596
597impl Tagged for PlanBlock {
598 const TAG: &'static str = "plan";
599}
600
601impl PlanBlock {
602 pub fn new(title: impl Into<String>, tasks: Vec<PlanTask>) -> Self {
603 Self {
604 r#type: Tag::default(),
605 title: title.into(),
606 tasks,
607 block_id: None,
608 }
609 }
610
611 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
612 self.block_id = Some(block_id.into());
613 self
614 }
615}
616
617#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
619pub struct PlanTask {
620 pub task_id: String,
621 pub title: String,
622 pub status: TaskStatus,
623 #[serde(skip_serializing_if = "Option::is_none")]
624 pub details: Option<RichTextBlock>,
625 #[serde(skip_serializing_if = "Option::is_none")]
626 pub output: Option<RichTextBlock>,
627 #[serde(default, skip_serializing_if = "Vec::is_empty")]
628 pub sources: Vec<UrlSourceElement>,
629}
630
631impl PlanTask {
632 pub fn new(task_id: impl Into<String>, title: impl Into<String>, status: TaskStatus) -> Self {
633 Self {
634 task_id: task_id.into(),
635 title: title.into(),
636 status,
637 details: None,
638 output: None,
639 sources: Vec::new(),
640 }
641 }
642
643 pub fn details(mut self, details: RichTextBlock) -> Self {
644 self.details = Some(details);
645 self
646 }
647
648 pub fn output(mut self, output: RichTextBlock) -> Self {
649 self.output = Some(output);
650 self
651 }
652
653 pub fn sources(mut self, sources: Vec<UrlSourceElement>) -> Self {
654 self.sources = sources;
655 self
656 }
657}
658
659#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
661pub struct SectionBlock {
662 r#type: Tag<Self>,
663 #[serde(skip_serializing_if = "Option::is_none")]
664 pub text: Option<TextObject>,
665 #[serde(skip_serializing_if = "Option::is_none")]
666 pub block_id: Option<String>,
667 #[serde(default, skip_serializing_if = "Vec::is_empty")]
668 pub fields: Vec<TextObject>,
669 #[serde(skip_serializing_if = "Option::is_none")]
670 pub accessory: Option<SectionAccessory>,
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub expand: Option<bool>,
673}
674
675impl Tagged for SectionBlock {
676 const TAG: &'static str = "section";
677}
678
679impl SectionBlock {
680 pub fn new() -> Self {
681 Self::default()
682 }
683
684 pub fn text(mut self, text: impl Into<TextObject>) -> Self {
685 self.text = Some(text.into());
686 self
687 }
688
689 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
690 self.block_id = Some(block_id.into());
691 self
692 }
693
694 pub fn fields(mut self, fields: Vec<TextObject>) -> Self {
695 self.fields = fields;
696 self
697 }
698
699 pub fn accessory(mut self, accessory: impl Into<SectionAccessory>) -> Self {
700 self.accessory = Some(accessory.into());
701 self
702 }
703
704 pub fn expand(mut self, expand: bool) -> Self {
705 self.expand = Some(expand);
706 self
707 }
708}
709
710#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
712pub struct TaskCardBlock {
713 r#type: Tag<Self>,
714 pub task_id: String,
715 pub title: String,
716 pub status: TaskStatus,
717 #[serde(skip_serializing_if = "Option::is_none")]
718 pub details: Option<RichTextBlock>,
719 #[serde(skip_serializing_if = "Option::is_none")]
720 pub output: Option<RichTextBlock>,
721 #[serde(default, skip_serializing_if = "Vec::is_empty")]
722 pub sources: Vec<UrlSourceElement>,
723 #[serde(skip_serializing_if = "Option::is_none")]
724 pub block_id: Option<String>,
725 #[serde(skip_serializing_if = "Option::is_none")]
726 pub icon: Option<SlackIcon>,
727 #[serde(skip_serializing_if = "Option::is_none")]
728 pub hide_title: Option<bool>,
729}
730
731impl Tagged for TaskCardBlock {
732 const TAG: &'static str = "task_card";
733}
734
735impl TaskCardBlock {
736 pub fn new(task_id: impl Into<String>, title: impl Into<String>, status: TaskStatus) -> Self {
737 Self {
738 r#type: Tag::default(),
739 task_id: task_id.into(),
740 title: title.into(),
741 status,
742 details: None,
743 output: None,
744 sources: Vec::new(),
745 block_id: None,
746 icon: None,
747 hide_title: None,
748 }
749 }
750
751 pub fn details(mut self, details: RichTextBlock) -> Self {
752 self.details = Some(details);
753 self
754 }
755
756 pub fn output(mut self, output: RichTextBlock) -> Self {
757 self.output = Some(output);
758 self
759 }
760
761 pub fn sources(mut self, sources: Vec<UrlSourceElement>) -> Self {
762 self.sources = sources;
763 self
764 }
765
766 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
767 self.block_id = Some(block_id.into());
768 self
769 }
770
771 pub fn icon(mut self, icon: SlackIcon) -> Self {
772 self.icon = Some(icon);
773 self
774 }
775
776 pub fn hide_title(mut self, hide_title: bool) -> Self {
777 self.hide_title = Some(hide_title);
778 self
779 }
780}
781
782#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
784pub struct VideoBlock {
785 r#type: Tag<Self>,
786 pub alt_text: String,
787 #[serde(skip_serializing_if = "Option::is_none")]
788 pub author_name: Option<String>,
789 #[serde(skip_serializing_if = "Option::is_none")]
790 pub block_id: Option<String>,
791 #[serde(skip_serializing_if = "Option::is_none")]
792 pub description: Option<TextObject>,
793 #[serde(skip_serializing_if = "Option::is_none")]
794 pub provider_icon_url: Option<String>,
795 #[serde(skip_serializing_if = "Option::is_none")]
796 pub provider_name: Option<String>,
797 pub title: TextObject,
798 #[serde(skip_serializing_if = "Option::is_none")]
799 pub title_url: Option<String>,
800 pub thumbnail_url: String,
801 pub video_url: String,
802}
803
804impl Tagged for VideoBlock {
805 const TAG: &'static str = "video";
806}
807
808impl VideoBlock {
809 pub fn new(
810 alt_text: impl Into<String>,
811 title: impl Into<TextObject>,
812 thumbnail_url: impl Into<String>,
813 video_url: impl Into<String>,
814 ) -> Self {
815 Self {
816 r#type: Tag::default(),
817 alt_text: alt_text.into(),
818 author_name: None,
819 block_id: None,
820 description: None,
821 provider_icon_url: None,
822 provider_name: None,
823 title: title.into(),
824 title_url: None,
825 thumbnail_url: thumbnail_url.into(),
826 video_url: video_url.into(),
827 }
828 }
829
830 pub fn author_name(mut self, author_name: impl Into<String>) -> Self {
831 self.author_name = Some(author_name.into());
832 self
833 }
834
835 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
836 self.block_id = Some(block_id.into());
837 self
838 }
839
840 pub fn description(mut self, description: impl Into<TextObject>) -> Self {
841 self.description = Some(description.into());
842 self
843 }
844
845 pub fn provider_icon_url(mut self, provider_icon_url: impl Into<String>) -> Self {
846 self.provider_icon_url = Some(provider_icon_url.into());
847 self
848 }
849
850 pub fn provider_name(mut self, provider_name: impl Into<String>) -> Self {
851 self.provider_name = Some(provider_name.into());
852 self
853 }
854
855 pub fn title_url(mut self, title_url: impl Into<String>) -> Self {
856 self.title_url = Some(title_url.into());
857 self
858 }
859}
860
861#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
863#[serde(rename_all = "snake_case")]
864pub enum AlertLevel {
865 Default,
866 Info,
867 Warning,
868 Error,
869 Success,
870 #[serde(untagged)]
871 Other(String),
872}
873
874#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
876#[serde(rename_all = "snake_case")]
877pub enum ContainerWidth {
878 Narrow,
879 Standard,
880 Wide,
881 Full,
882 #[serde(untagged)]
883 Other(String),
884}
885
886#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
888#[serde(rename_all = "snake_case")]
889pub enum TaskStatus {
890 Pending,
891 InProgress,
892 Complete,
893 Error,
894 #[serde(untagged)]
895 Other(String),
896}