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}