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}