twilight_util/builder/message/
select_menu.rs

1use twilight_model::channel::{
2    message::{
3        component::{SelectDefaultValue, SelectMenu, SelectMenuOption, SelectMenuType},
4        EmojiReactionType,
5    },
6    ChannelType,
7};
8
9/// Create a select menu option with a builder
10#[derive(Clone, Debug, Eq, PartialEq)]
11#[must_use = "must be built into a select menu option"]
12pub struct SelectMenuOptionBuilder(SelectMenuOption);
13
14impl SelectMenuOptionBuilder {
15    /// Create a new select menu option builder.
16    pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
17        Self(SelectMenuOption {
18            default: false,
19            description: None,
20            emoji: None,
21            label: label.into(),
22            value: value.into(),
23        })
24    }
25
26    /// Set whether this option is the default
27    pub const fn default(mut self, default: bool) -> Self {
28        self.0.default = default;
29
30        self
31    }
32
33    /// Set the associated emoji
34    pub fn emoji(mut self, emoji: EmojiReactionType) -> Self {
35        self.0.emoji.replace(emoji);
36
37        self
38    }
39
40    /// Set the description
41    pub fn description(mut self, description: impl Into<String>) -> Self {
42        self.0.description.replace(description.into());
43
44        self
45    }
46
47    /// Build into a select menu option
48    pub fn build(self) -> SelectMenuOption {
49        self.0
50    }
51}
52
53impl From<SelectMenuOptionBuilder> for SelectMenuOption {
54    fn from(builder: SelectMenuOptionBuilder) -> Self {
55        builder.build()
56    }
57}
58
59/// Create a select menu with a builder.
60#[derive(Clone, Debug, Eq, PartialEq)]
61#[must_use = "must be built into a select menu"]
62pub struct SelectMenuBuilder(SelectMenu);
63
64impl SelectMenuBuilder {
65    /// Create a new select menu builder.
66    pub fn new(custom_id: impl Into<String>, kind: SelectMenuType) -> Self {
67        Self(SelectMenu {
68            custom_id: custom_id.into(),
69            disabled: false,
70            max_values: None,
71            min_values: None,
72            options: None,
73            placeholder: None,
74            id: None,
75            channel_types: None,
76            default_values: None,
77            kind,
78        })
79    }
80
81    /// Set whether this select menu is disabled.
82    pub const fn disabled(mut self, disabled: bool) -> Self {
83        self.0.disabled = disabled;
84
85        self
86    }
87
88    /// Set the max values of this select menu.
89    pub fn max_values(mut self, max_values: u8) -> Self {
90        self.0.max_values.replace(max_values);
91
92        self
93    }
94
95    /// Set the min values of this select menu.
96    pub fn min_values(mut self, min_values: u8) -> Self {
97        self.0.min_values.replace(min_values);
98
99        self
100    }
101
102    /// Add an option to this select menu.
103    #[allow(clippy::missing_panics_doc)] // this does not panic; unwrap is never called on None
104    pub fn option(mut self, option: impl Into<SelectMenuOption>) -> Self {
105        if self.0.options.is_none() {
106            self.0.options.replace(Vec::new());
107        }
108
109        self.0.options.as_mut().unwrap().push(option.into());
110
111        self
112    }
113
114    /// Set the placeholder for this select menu.
115    pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
116        self.0.placeholder.replace(placeholder.into());
117
118        self
119    }
120
121    /// Set the identifier of this select menu.
122    pub fn id(mut self, id: impl Into<i32>) -> Self {
123        self.0.id.replace(id.into());
124
125        self
126    }
127
128    /// Set the channel types of this select menu.
129    pub fn channel_types(mut self, channel_types: Vec<ChannelType>) -> Self {
130        self.0.channel_types.replace(channel_types);
131
132        self
133    }
134
135    /// Set the default values of this select menu.
136    pub fn default_values(mut self, default_values: Vec<SelectDefaultValue>) -> Self {
137        self.0.default_values.replace(default_values);
138
139        self
140    }
141
142    /// Build into a select menu,
143    pub fn build(self) -> SelectMenu {
144        self.0
145    }
146}
147
148impl From<SelectMenuBuilder> for SelectMenu {
149    fn from(builder: SelectMenuBuilder) -> Self {
150        builder.build()
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use static_assertions::assert_impl_all;
158    use std::fmt::Debug;
159
160    assert_impl_all!(SelectMenuBuilder: Clone, Debug, Eq, PartialEq, Send, Sync);
161    assert_impl_all!(SelectMenu: From<SelectMenuBuilder>);
162
163    #[test]
164    fn builder() {
165        let expected_option = SelectMenuOption {
166            default: false,
167            description: Some("test".to_string()),
168            emoji: None,
169            label: "bar".to_string(),
170            value: "foo".to_string(),
171        };
172
173        let expected = SelectMenu {
174            custom_id: "foo".to_string(),
175            disabled: false,
176            max_values: None,
177            min_values: None,
178            options: Some(vec![expected_option]),
179            placeholder: None,
180            id: None,
181            channel_types: None,
182            default_values: None,
183            kind: SelectMenuType::Text,
184        };
185
186        let actual = SelectMenuBuilder::new("foo", SelectMenuType::Text)
187            .option(SelectMenuOptionBuilder::new("bar", "foo").description("test"))
188            .build();
189
190        assert_eq!(actual, expected);
191    }
192}