slack_blocks/elems/image.rs
1//! # Image Element
2//!
3//! An element to insert an image as part of a larger block of content.
4//!
5//! If you want a block with _only_ an image in it, you're looking for the [`image` block 🔗].
6//!
7//! [slack api docs 🔗]
8//!
9//! Works in [blocks 🔗]: Section, Context
10//!
11//! [`image` block 🔗]: https://api.slack.com/reference/block-kit/blocks#image
12//! [slack api docs 🔗]: https://api.slack.com/reference/block-kit/block-elements#radio
13//! [blocks 🔗]: https://api.slack.com/reference/block-kit/blocks
14
15use std::borrow::Cow;
16
17use serde::{Deserialize as De, Serialize as Ser};
18#[cfg(feature = "validation")]
19use validator::Validate;
20
21#[cfg(feature = "validation")]
22use crate::val_helpr::ValidationResult;
23
24/// # Image Element
25///
26/// An element to insert an image as part of a larger block of content.
27///
28/// If you want a block with _only_ an image in it, you're looking for the [`image` block 🔗].
29///
30/// [slack api docs 🔗]
31///
32/// Works in [blocks 🔗]: Section, Context
33///
34/// [`image` block 🔗]: https://api.slack.com/reference/block-kit/blocks#image
35/// [slack api docs 🔗]: https://api.slack.com/reference/block-kit/block-elements#radio
36/// [blocks 🔗]: https://api.slack.com/reference/block-kit/blocks
37#[derive(Clone, Debug, Hash, PartialEq, Ser, De)]
38#[cfg_attr(feature = "validation", derive(Validate))]
39pub struct Image<'a> {
40 image_url: Cow<'a, str>,
41 alt_text: Cow<'a, str>,
42}
43
44impl<'a> Image<'a> {
45 /// Build a new Image element.
46 ///
47 /// # Example
48 /// see example for `build::ImageBuilder`.
49 pub fn builder() -> build::ImageBuilderInit<'a> {
50 build::ImageBuilderInit::new()
51 }
52
53 /// Validate that this image element agrees with Slack's model requirements.
54 ///
55 /// No rules are specified in the Slack docs at the time of writing so this will always succeed.
56 #[cfg(feature = "validation")]
57 #[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
58 pub fn validate(&self) -> ValidationResult {
59 Ok(())
60 }
61}
62
63/// Image element builder
64pub mod build {
65 use std::marker::PhantomData;
66
67 use super::*;
68 use crate::build::*;
69
70 /// Required builder methods
71 #[allow(non_camel_case_types)]
72 pub mod method {
73 /// ImageBuilder.image_url
74 #[derive(Copy, Clone, Debug)]
75 pub struct image_url;
76 /// ImageBuilder.alt_text
77 #[derive(Copy, Clone, Debug)]
78 pub struct alt_text;
79 }
80
81 /// Initial state for Image Builder
82 pub type ImageBuilderInit<'a> =
83 ImageBuilder<'a,
84 RequiredMethodNotCalled<method::image_url>,
85 RequiredMethodNotCalled<method::alt_text>>;
86
87 /// Image Element builder
88 ///
89 /// Allows you to construct safely, with compile-time checks
90 /// on required setter methods.
91 ///
92 /// # Required Methods
93 /// `ImageBuilder::build()` is only available if these methods have been called:
94 /// - `image_url`
95 /// - `alt_text`
96 ///
97 /// # Example
98 /// ```
99 /// use slack_blocks::{blocks::{Block, Context},
100 /// elems::Image};
101 ///
102 /// let img = Image::builder().image_url("foo").alt_text("bar").build();
103 ///
104 /// let block: Block = Context::builder().element(img).build().into();
105 ///
106 /// // <send block to slack API>
107 /// ```
108 #[derive(Debug)]
109 pub struct ImageBuilder<'a, U, A> {
110 image_url: Option<Cow<'a, str>>,
111 alt_text: Option<Cow<'a, str>>,
112 state: PhantomData<(U, A)>,
113 }
114
115 impl<'a, U, A> ImageBuilder<'a, U, A> {
116 /// Construct a new builder
117 pub fn new() -> Self {
118 Self { image_url: None,
119 alt_text: None,
120 state: PhantomData::<_> }
121 }
122
123 /// Set `image_url` (**Required**)
124 ///
125 /// The URL of the image to be displayed.
126 pub fn image_url<S>(self,
127 image_url: S)
128 -> ImageBuilder<'a, Set<method::image_url>, A>
129 where S: Into<Cow<'a, str>>
130 {
131 ImageBuilder { image_url: Some(image_url.into()),
132 alt_text: self.alt_text,
133 state: PhantomData::<_> }
134 }
135
136 /// Alias of `image_url`.
137 pub fn src<S>(self,
138 image_url: S)
139 -> ImageBuilder<'a, Set<method::image_url>, A>
140 where S: Into<Cow<'a, str>>
141 {
142 self.image_url(image_url)
143 }
144
145 /// Set `alt_text` (**Required**)
146 ///
147 /// A plain-text summary of the image.
148 ///
149 /// This should not contain any markup.
150 pub fn alt_text<S>(self,
151 alt_text: S)
152 -> ImageBuilder<'a, U, Set<method::alt_text>>
153 where S: Into<Cow<'a, str>>
154 {
155 ImageBuilder { image_url: self.image_url,
156 alt_text: Some(alt_text.into()),
157 state: PhantomData::<_> }
158 }
159
160 /// Alias of `alt_text`.
161 pub fn alt<S>(self,
162 alt_text: S)
163 -> ImageBuilder<'a, U, Set<method::alt_text>>
164 where S: Into<Cow<'a, str>>
165 {
166 self.alt_text(alt_text)
167 }
168 }
169
170 impl<'a> ImageBuilder<'a, Set<method::image_url>, Set<method::alt_text>> {
171 /// All done building, now give me a darn image element!
172 ///
173 /// > `no method name 'build' found for struct 'ImageBuilder<...>'`?
174 /// Make sure all required setter methods have been called. See docs for `ImageBuilder`.
175 ///
176 /// ```compile_fail
177 /// use slack_blocks::elems::Image;
178 ///
179 /// let foo = Image::builder().build(); // Won't compile!
180 /// ```
181 ///
182 /// ```
183 /// use slack_blocks::{compose::Opt, elems::Image};
184 ///
185 /// let foo = Image::builder().image_url("https://foo.com/bar.png")
186 /// .alt_text("pic of bar")
187 /// .build();
188 /// ```
189 pub fn build(self) -> Image<'a> {
190 Image { image_url: self.image_url.unwrap(),
191 alt_text: self.alt_text.unwrap() }
192 }
193 }
194}