slack_blocks/elems/select/
public_channel.rs

1//! # Public Channel Select
2//! [slack api docs 🔗](https://api.slack.com/reference/block-kit/block-elements#channel_select)
3//!
4//! This select menu will populate its options with a list of
5//! public channels visible to the current user in the active workspace.
6
7use std::borrow::Cow;
8
9use serde::{Deserialize, Serialize};
10#[cfg(feature = "validation")]
11use validator::Validate;
12
13#[cfg(feature = "validation")]
14use crate::val_helpr::ValidationResult;
15use crate::{compose::Confirm, text};
16
17/// # Public Channel Select
18/// [slack api docs 🔗](https://api.slack.com/reference/block-kit/block-elements#channel_select)
19///
20/// This select menu will populate its options with a list of
21/// public channels visible to the current user in the active workspace.
22#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
23#[cfg_attr(feature = "validation", derive(Validate))]
24pub struct PublicChannel<'a> {
25  #[cfg_attr(feature = "validation",
26             validate(custom = "super::validate::placeholder"))]
27  placeholder: text::Text,
28
29  #[cfg_attr(feature = "validation", validate(length(max = 255)))]
30  action_id: Cow<'a, str>,
31
32  #[serde(skip_serializing_if = "Option::is_none")]
33  #[cfg_attr(feature = "validation", validate)]
34  confirm: Option<Confirm>,
35
36  #[serde(skip_serializing_if = "Option::is_none")]
37  initial_channel: Option<Cow<'a, str>>,
38}
39
40impl<'a> PublicChannel<'a> {
41  /// Build a new public channel select element
42  ///
43  /// # Examples
44  /// ```
45  /// use std::convert::TryFrom;
46  ///
47  /// use slack_blocks::{blocks::{Actions, Block},
48  ///                    compose::Opt,
49  ///                    elems::{select, BlockElement},
50  ///                    text};
51  ///
52  /// let select =
53  ///   select::PublicChannel::builder().placeholder("Choose your favorite channel!")
54  ///                                  .action_id("fave_channel")
55  ///                                  .build();
56  ///
57  /// let block: Block = Actions::builder().element(select).build().into();
58  /// ```
59  pub fn builder() -> build::PublicChannelBuilderInit<'a> {
60    build::PublicChannelBuilderInit::new()
61  }
62
63  /// Validate that this Public Channel Select element
64  /// agrees with Slack's model requirements
65  ///
66  /// # Errors
67  /// - If `from_placeholder_and_action_id` was called with
68  ///     `placeholder` longer than 150 chars
69  /// - If `from_placeholder_and_action_id` was called with
70  ///     `action_id` longer than 255 chars
71  ///
72  /// # Example
73  /// ```
74  /// use slack_blocks::elems::select;
75  ///
76  /// let select = select::PublicChannel::builder().placeholder(
77  ///                           r#"Hey I really would appreciate it if you chose
78  ///         a channel relatively soon, so that we can figure out
79  ///         where we need to send this poll, ok? it's kind of
80  ///         important that you specify where this poll should be
81  ///         sent, in case we haven't made that super clear.
82  ///         If you understand, could you pick a channel, already??"#,
83  /// )
84  ///              .action_id("ABC123")
85  ///              .build();
86  ///
87  /// assert!(matches!(select.validate(), Err(_)))
88  /// ```
89  #[cfg(feature = "validation")]
90  #[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
91  pub fn validate(&self) -> ValidationResult {
92    Validate::validate(&self)
93  }
94}
95
96/// Public Channel Select Builder
97pub mod build {
98  use std::marker::PhantomData;
99
100  use super::*;
101  use crate::{build::*,
102              elems::select::{multi, select_kind}};
103
104  /// Required builder methods
105  #[allow(non_camel_case_types)]
106  pub mod method {
107    /// PublicChannelBuilder.placeholder
108    #[derive(Copy, Clone, Debug)]
109    pub struct placeholder;
110
111    /// PublicChannelBuilder.action_id
112    #[derive(Copy, Clone, Debug)]
113    pub struct action_id;
114  }
115
116  /// PublicChannel Select builder
117  ///
118  /// Allows you to construct a PublicChannel Select safely, with compile-time checks
119  /// on required setter methods.
120  ///
121  /// # Required Methods
122  /// `PublicChannelBuilder::build()` is only available if these methods have been called:
123  ///  - `placeholder`
124  ///  - `action_id`
125  ///
126  /// NOTE: I'm experimenting with an API that deviates from the `from_foo_and_bar`.
127  ///       If you're a user of this library, please give me feedback in the repository
128  ///       as to which pattern you like more. This will most likely be the new builder pattern
129  ///       for every structure in this crate.
130  ///
131  /// # Example
132  /// ```
133  /// use std::convert::TryFrom;
134  ///
135  /// use slack_blocks::{blocks::{Actions, Block},
136  ///                    compose::Opt,
137  ///                    elems::{select::PublicChannel, BlockElement}};
138  ///
139  /// let select =
140  ///   PublicChannel::builder().placeholder("Choose your favorite channel!")
141  ///                           .action_id("favorite_channel")
142  ///                           .build();
143  ///
144  /// let block: Block = Actions::builder().element(select).build().into();
145  ///
146  /// // <send block to API>
147  /// ```
148  #[derive(Debug)]
149  pub struct PublicChannelBuilder<'a, Multi, Placeholder, ActionId> {
150    placeholder: Option<text::Text>,
151    action_id: Option<Cow<'a, str>>,
152    confirm: Option<Confirm>,
153    initial_channel: Option<Cow<'a, str>>,
154    initial_channels: Option<Cow<'a, [String]>>,
155    max_selected_items: Option<u32>,
156    state: PhantomData<(Multi, Placeholder, ActionId)>,
157  }
158
159  /// Initial state for PublicChannelBuilder.
160  ///
161  /// Users will be able to choose one of the options.
162  ///
163  /// To allow choosing many, use `slack_blocks::elems::select::multi::PublicChannel::builder`.
164  pub type PublicChannelBuilderInit<'a> =
165    PublicChannelBuilder<'a,
166                         select_kind::Single,
167                         RequiredMethodNotCalled<method::placeholder>,
168                         RequiredMethodNotCalled<method::action_id>>;
169
170  /// Initial state for PublicChannelBuilder.
171  ///
172  /// Users will be able to choose many of the options.
173  pub type MultiPublicChannelBuilderInit<'a> =
174    PublicChannelBuilder<'a,
175                         select_kind::Multi,
176                         RequiredMethodNotCalled<method::placeholder>,
177                         RequiredMethodNotCalled<method::action_id>>;
178
179  // Methods that are always available
180  impl<'a, M, P, A> PublicChannelBuilder<'a, M, P, A> {
181    /// Construct a new PublicChannelBuilder
182    pub fn new() -> Self {
183      Self { placeholder: None,
184             action_id: None,
185             initial_channel: None,
186             initial_channels: None,
187             max_selected_items: None,
188             confirm: None,
189             state: PhantomData::<_> }
190    }
191
192    /// Change the marker type params to some other arbitrary marker type params
193    fn cast_state<P2, A2>(self) -> PublicChannelBuilder<'a, M, P2, A2> {
194      PublicChannelBuilder { placeholder: self.placeholder,
195                             action_id: self.action_id,
196                             confirm: self.confirm,
197                             initial_channel: self.initial_channel,
198                             initial_channels: self.initial_channels,
199                             max_selected_items: self.max_selected_items,
200                             state: PhantomData::<_> }
201    }
202
203    /// Set `placeholder` (**Required**)
204    ///
205    /// A [`plain_text` only text object 🔗] that defines
206    /// the placeholder text shown on the menu.
207    /// Maximum length for the `text` in this field is 150 characters.
208    ///
209    /// [`plain_text` only text object 🔗]: https://api.slack.comhttps://api.slack.com/reference/block-kit/composition-objects#text
210    pub fn placeholder(
211      mut self,
212      text: impl Into<text::Plain>)
213      -> PublicChannelBuilder<'a, M, Set<method::placeholder>, A> {
214      self.placeholder = Some(text.into().into());
215      self.cast_state()
216    }
217
218    /// Set `action_id` (**Required**)
219    ///
220    /// An identifier for the action triggered when a menu option is selected.
221    /// You can use this when you receive an interaction payload to [identify the source of the action 🔗].
222    /// Should be unique among all other `action_id`s used elsewhere by your app.
223    /// Maximum length for this field is 255 characters.
224    ///
225    /// [identify the source of the action 🔗]: https://api.slack.comhttps://api.slack.com/interactivity/handling#payloads
226    pub fn action_id(
227      mut self,
228      text: impl Into<Cow<'a, str>>)
229      -> PublicChannelBuilder<'a, M, P, Set<method::action_id>> {
230      self.action_id = Some(text.into());
231      self.cast_state()
232    }
233
234    /// Set `confirm` (Optional)
235    ///
236    /// A [confirm object 🔗] that defines an
237    /// optional confirmation dialog that appears after
238    /// a menu item is selected.
239    ///
240    /// [confirm object 🔗]: https://api.slack.comhttps://api.slack.com/reference/block-kit/composition-objects#confirm
241    pub fn confirm(mut self, confirm: Confirm) -> Self {
242      self.confirm = Some(confirm);
243      self
244    }
245  }
246
247  impl<'a, M, P, A> PublicChannelBuilder<'a, M, P, A> {
248    /// Set `initial_channel` (Optional)
249    ///
250    /// The ID of any valid conversation to be pre-selected when the menu loads.
251    ///
252    /// If `default_to_current_conversation` is called, this will take precedence.
253    pub fn initial_channel<S>(mut self, channel: S) -> Self
254      where S: Into<Cow<'a, str>>
255    {
256      self.initial_channel = Some(channel.into());
257      self
258    }
259
260    /// Set `initial_channel` (Optional, exclusive with `initial_channel_current`)
261    ///
262    /// A collection of IDs of any valid conversations to be pre-selected when the menu loads.
263    pub fn initial_channels<S>(mut self, channels: S) -> Self
264      where S: Into<Cow<'a, [String]>>
265    {
266      self.initial_channels = Some(channels.into());
267      self.cast_state()
268    }
269  }
270
271  impl<'a, P, A> PublicChannelBuilder<'a, select_kind::Multi, P, A> {
272    /// Set `max_selected_items` (Optional)
273    ///
274    /// Specifies the maximum number of items that can be selected in the menu.
275    ///
276    /// Minimum number is 1.
277    pub fn max_selected_items(mut self, max: u32) -> Self {
278      self.max_selected_items = Some(max);
279      self
280    }
281  }
282
283  impl<'a>
284    PublicChannelBuilder<'a,
285                         select_kind::Single,
286                         Set<method::placeholder>,
287                         Set<method::action_id>>
288  {
289    /// All done building, now give me a darn select element!
290    ///
291    /// > `no method name 'build' found for struct 'select::static_::build::PublicChannelBuilder<...>'`?
292    /// Make sure all required setter methods have been called. See docs for `PublicChannelBuilder`.
293    ///
294    /// ```compile_fail
295    /// use slack_blocks::elems::select::PublicChannel;
296    ///
297    /// let sel = PublicChannel::builder().build(); // Won't compile!
298    /// ```
299    ///
300    /// ```
301    /// use slack_blocks::elems::select::PublicChannel;
302    ///
303    /// let sel = PublicChannel::builder().placeholder("foo")
304    ///                                   .action_id("bar")
305    ///                                   .build();
306    /// ```
307    pub fn build(self) -> PublicChannel<'a> {
308      PublicChannel { placeholder: self.placeholder.unwrap(),
309                      action_id: self.action_id.unwrap(),
310                      confirm: self.confirm,
311                      initial_channel: self.initial_channel }
312    }
313  }
314
315  impl<'a>
316    PublicChannelBuilder<'a,
317                         select_kind::Multi,
318                         Set<method::placeholder>,
319                         Set<method::action_id>>
320  {
321    /// All done building, now give me a darn select element!
322    ///
323    /// > `no method name 'build' found for struct 'select::static_::build::PublicChannelBuilder<...>'`?
324    /// Make sure all required setter methods have been called. See docs for `PublicChannelBuilder`.
325    ///
326    /// ```compile_fail
327    /// use slack_blocks::elems::select;
328    ///
329    /// let sel = select::multi::PublicChannel::builder().build(); // Won't compile!
330    /// ```
331    ///
332    /// ```
333    /// use slack_blocks::elems::select;
334    ///
335    /// let sel = select::multi::PublicChannel::builder().placeholder("foo")
336    ///                                                  .action_id("bar")
337    ///                                                  .build();
338    /// ```
339    pub fn build(self) -> multi::PublicChannel<'a> {
340      multi::PublicChannel { placeholder: self.placeholder.unwrap(),
341                             action_id: self.action_id.unwrap(),
342                             confirm: self.confirm,
343                             initial_channels: self.initial_channels,
344                             max_selected_items: self.max_selected_items }
345    }
346  }
347}