slack_blocks/elems/select/
user.rs

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