1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
use serde::{Deserialize, Serialize}; use validator::Validate; use super::text; use super::Opt; use crate::val_helpr::ValidationResult; /// # Option Group /// [slack api docs 🔗] /// /// Provides a way to group options in a [select menu 🔗] or [multi-select menu 🔗]. /// /// [select menu 🔗]: https://api.slack.com/reference/block-kit/block-elements#select /// [multi-select menu 🔗]: https://api.slack.com/reference/block-kit/block-elements#multi_select /// [slack api docs 🔗]: https://api.slack.com/reference/block-kit/composition-objects#option_group /// [`plain_text` only text object 🔗]: https://api.slack.com/reference/block-kit/composition-objects#text #[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize, Validate)] pub struct OptGroup<M = ()> { #[validate(custom = "validate::label")] label: text::Text, #[validate(length(max = 100))] options: Vec<Opt<M>>, } impl OptGroup<()> { /// Construct an Option Group from a label and /// collection of options in the group /// /// # Arguments /// - `label` - A [`plain_text` only text object 🔗] that defines /// the label shown above this group of options. /// Maximum length for the `text` in this field is 75 characters. /// - `opts` - An array of [option objects 🔗] that belong to /// this specific group. Maximum of 100 items. /// /// [option objects 🔗]: https://api.slack.comCURRENT_PAGEoption /// [`plain_text` only text object 🔗]: https://api.slack.comCURRENT_PAGEtext /// /// # Example /// ``` /// use slack_blocks::blocks::Block; /// use slack_blocks::blocks::section::Contents as Section; /// use slack_blocks::blocks::actions::Contents as Actions; /// use slack_blocks::text::{Mrkdwn}; /// use slack_blocks::compose::{OptGroup, Opt}; /// /// let prompt = "Choose your favorite city from each state!"; /// /// let blocks: Vec<Block> = vec![ /// Section::from_text(Mrkdwn::from(prompt)).into(), /// // TODO: insert option group once block elements are in place /// Actions::from_action_elements(vec![]).into(), /// ]; /// /// let groups: Vec<OptGroup<_>> = vec![ /// OptGroup::from_label_and_opts( /// "Arizona", /// vec![ /// Opt::from_mrkdwn_and_value("Phoenix", "az_phx"), /// // etc... /// ] /// ), /// OptGroup::from_label_and_opts( /// "California", /// vec![ /// Opt::from_mrkdwn_and_value("San Diego", "ca_sd"), /// // etc... /// ] /// ), /// ]; /// ``` pub fn from_label_and_opts<M>( label: impl Into<text::Plain>, options: impl IntoIterator<Item = Opt<M>>, ) -> OptGroup<M> { OptGroup::<M> { label: label.into().into(), options: options.into_iter().collect(), } } } impl<M> OptGroup<M> { /// Validate that this Option Group object /// agrees with Slack's model requirements /// /// # Errors /// - If `from_label_and_opts` was called with `label` /// longer than 75 chars /// - If `from_label_and_opts` was called with /// more than 100 options /// /// # Example /// ``` /// use slack_blocks::compose::{OptGroup, Opt}; /// use std::iter::repeat; /// /// let long_string: String = repeat(' ').take(76).collect(); /// /// let opt = Opt::from_mrkdwn_and_value("San Diego", "ca_sd"); /// let grp = OptGroup::from_label_and_opts(long_string, vec![opt]); /// /// assert_eq!(true, matches!(grp.validate(), Err(_))); /// ``` pub fn validate(&self) -> ValidationResult { Validate::validate(self) } } mod validate { use super::*; use crate::val_helpr::{below_len, ValidatorResult}; pub fn label(text: &text::Text) -> ValidatorResult { below_len("Option Group Label", 75, text.as_ref()) } }