twilight_util/builder/message/
select_menu.rs1use twilight_model::channel::{
2 message::{
3 component::{SelectDefaultValue, SelectMenu, SelectMenuOption, SelectMenuType},
4 EmojiReactionType,
5 },
6 ChannelType,
7};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
11#[must_use = "must be built into a select menu option"]
12pub struct SelectMenuOptionBuilder(SelectMenuOption);
13
14impl SelectMenuOptionBuilder {
15 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 pub const fn default(mut self, default: bool) -> Self {
28 self.0.default = default;
29
30 self
31 }
32
33 pub fn emoji(mut self, emoji: EmojiReactionType) -> Self {
35 self.0.emoji.replace(emoji);
36
37 self
38 }
39
40 pub fn description(mut self, description: impl Into<String>) -> Self {
42 self.0.description.replace(description.into());
43
44 self
45 }
46
47 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#[derive(Clone, Debug, Eq, PartialEq)]
61#[must_use = "must be built into a select menu"]
62pub struct SelectMenuBuilder(SelectMenu);
63
64impl SelectMenuBuilder {
65 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 pub const fn disabled(mut self, disabled: bool) -> Self {
83 self.0.disabled = disabled;
84
85 self
86 }
87
88 pub fn max_values(mut self, max_values: u8) -> Self {
90 self.0.max_values.replace(max_values);
91
92 self
93 }
94
95 pub fn min_values(mut self, min_values: u8) -> Self {
97 self.0.min_values.replace(min_values);
98
99 self
100 }
101
102 #[allow(clippy::missing_panics_doc)] 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 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
116 self.0.placeholder.replace(placeholder.into());
117
118 self
119 }
120
121 pub fn id(mut self, id: impl Into<i32>) -> Self {
123 self.0.id.replace(id.into());
124
125 self
126 }
127
128 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 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 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}