1use serde::Serialize;
2
3use crate::model::prelude::*;
4
5#[derive(Clone, Debug, PartialEq)]
9#[must_use]
10pub enum CreateActionRow {
11 Buttons(Vec<CreateButton>),
12 SelectMenu(CreateSelectMenu),
13 InputText(CreateInputText),
15}
16
17impl serde::Serialize for CreateActionRow {
18 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
19 use serde::ser::SerializeMap as _;
20
21 let mut map = serializer.serialize_map(Some(2))?;
22 map.serialize_entry("type", &1_u8)?;
23
24 match self {
25 CreateActionRow::Buttons(buttons) => map.serialize_entry("components", &buttons)?,
26 CreateActionRow::SelectMenu(select) => map.serialize_entry("components", &[select])?,
27 CreateActionRow::InputText(input) => map.serialize_entry("components", &[input])?,
28 }
29
30 map.end()
31 }
32}
33
34#[derive(Clone, Debug, Serialize, PartialEq)]
36#[must_use]
37pub struct CreateButton(Button);
38
39impl CreateButton {
40 pub fn new_link(url: impl Into<String>) -> Self {
45 Self(Button {
46 kind: ComponentType::Button,
47 data: ButtonKind::Link {
48 url: url.into(),
49 },
50 label: None,
51 emoji: None,
52 disabled: false,
53 })
54 }
55
56 pub fn new_premium(sku_id: impl Into<SkuId>) -> Self {
60 Self(Button {
61 kind: ComponentType::Button,
62 data: ButtonKind::Premium {
63 sku_id: sku_id.into(),
64 },
65 label: None,
66 emoji: None,
67 disabled: false,
68 })
69 }
70
71 pub fn new(custom_id: impl Into<String>) -> Self {
74 Self(Button {
75 kind: ComponentType::Button,
76 data: ButtonKind::NonLink {
77 style: ButtonStyle::Primary,
78 custom_id: custom_id.into(),
79 },
80 label: None,
81 emoji: None,
82 disabled: false,
83 })
84 }
85
86 pub fn custom_id(mut self, id: impl Into<String>) -> Self {
91 if let ButtonKind::NonLink {
92 custom_id, ..
93 } = &mut self.0.data
94 {
95 *custom_id = id.into();
96 }
97 self
98 }
99
100 pub fn style(mut self, new_style: ButtonStyle) -> Self {
104 if let ButtonKind::NonLink {
105 style, ..
106 } = &mut self.0.data
107 {
108 *style = new_style;
109 }
110 self
111 }
112
113 pub fn label(mut self, label: impl Into<String>) -> Self {
115 self.0.label = Some(label.into());
116 self
117 }
118
119 pub fn emoji(mut self, emoji: impl Into<ReactionType>) -> Self {
121 self.0.emoji = Some(emoji.into());
122 self
123 }
124
125 pub fn disabled(mut self, disabled: bool) -> Self {
127 self.0.disabled = disabled;
128 self
129 }
130}
131
132impl From<Button> for CreateButton {
133 fn from(button: Button) -> Self {
134 Self(button)
135 }
136}
137
138struct CreateSelectMenuDefault(Mention);
139
140impl Serialize for CreateSelectMenuDefault {
141 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
142 use serde::ser::SerializeMap as _;
143
144 let (id, kind) = match self.0 {
145 Mention::Channel(c) => (c.get(), "channel"),
146 Mention::Role(r) => (r.get(), "role"),
147 Mention::User(u) => (u.get(), "user"),
148 };
149
150 let mut map = serializer.serialize_map(Some(2))?;
151 map.serialize_entry("id", &id)?;
152 map.serialize_entry("type", kind)?;
153 map.end()
154 }
155}
156
157#[derive(Clone, Debug, PartialEq)]
159pub enum CreateSelectMenuKind {
160 String { options: Vec<CreateSelectMenuOption> },
161 User { default_users: Option<Vec<UserId>> },
162 Role { default_roles: Option<Vec<RoleId>> },
163 Mentionable { default_users: Option<Vec<UserId>>, default_roles: Option<Vec<RoleId>> },
164 Channel { channel_types: Option<Vec<ChannelType>>, default_channels: Option<Vec<ChannelId>> },
165}
166
167impl Serialize for CreateSelectMenuKind {
168 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169 #[derive(Serialize)]
170 struct Json<'a> {
171 #[serde(rename = "type")]
172 kind: u8,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 options: Option<&'a [CreateSelectMenuOption]>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 channel_types: Option<&'a [ChannelType]>,
177 #[serde(skip_serializing_if = "Vec::is_empty")]
178 default_values: Vec<CreateSelectMenuDefault>,
179 }
180
181 #[allow(clippy::ref_option)]
182 fn map<I: Into<Mention> + Copy>(
183 values: &Option<Vec<I>>,
184 ) -> impl Iterator<Item = CreateSelectMenuDefault> + '_ {
185 values.iter().flatten().map(|&i| CreateSelectMenuDefault(i.into()))
187 }
188
189 #[rustfmt::skip]
190 let default_values = match self {
191 Self::String { .. } => vec![],
192 Self::User { default_users: default_values } => map(default_values).collect(),
193 Self::Role { default_roles: default_values } => map(default_values).collect(),
194 Self::Mentionable { default_users, default_roles } => {
195 let users = map(default_users);
196 let roles = map(default_roles);
197 users.chain(roles).collect()
198 },
199 Self::Channel { channel_types: _, default_channels: default_values } => map(default_values).collect(),
200 };
201
202 #[rustfmt::skip]
203 let json = Json {
204 kind: match self {
205 Self::String { .. } => 3,
206 Self::User { .. } => 5,
207 Self::Role { .. } => 6,
208 Self::Mentionable { .. } => 7,
209 Self::Channel { .. } => 8,
210 },
211 options: match self {
212 Self::String { options } => Some(options),
213 _ => None,
214 },
215 channel_types: match self {
216 Self::Channel { channel_types, default_channels: _ } => channel_types.as_deref(),
217 _ => None,
218 },
219 default_values,
220 };
221
222 json.serialize(serializer)
223 }
224}
225
226#[derive(Clone, Debug, Serialize, PartialEq)]
230#[must_use]
231pub struct CreateSelectMenu {
232 custom_id: String,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 placeholder: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 min_values: Option<u8>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 max_values: Option<u8>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 disabled: Option<bool>,
241
242 #[serde(flatten)]
243 kind: CreateSelectMenuKind,
244}
245
246impl CreateSelectMenu {
247 pub fn new(custom_id: impl Into<String>, kind: CreateSelectMenuKind) -> Self {
250 Self {
251 custom_id: custom_id.into(),
252 placeholder: None,
253 min_values: None,
254 max_values: None,
255 disabled: None,
256 kind,
257 }
258 }
259
260 pub fn placeholder(mut self, label: impl Into<String>) -> Self {
262 self.placeholder = Some(label.into());
263 self
264 }
265
266 pub fn custom_id(mut self, id: impl Into<String>) -> Self {
269 self.custom_id = id.into();
270 self
271 }
272
273 pub fn min_values(mut self, min: u8) -> Self {
275 self.min_values = Some(min);
276 self
277 }
278
279 pub fn max_values(mut self, max: u8) -> Self {
281 self.max_values = Some(max);
282 self
283 }
284
285 pub fn disabled(mut self, disabled: bool) -> Self {
287 self.disabled = Some(disabled);
288 self
289 }
290}
291
292#[derive(Clone, Debug, Serialize, PartialEq)]
296#[must_use]
297pub struct CreateSelectMenuOption {
298 label: String,
299 value: String,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 description: Option<String>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 emoji: Option<ReactionType>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 default: Option<bool>,
306}
307
308impl CreateSelectMenuOption {
309 pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
312 Self {
313 label: label.into(),
314 value: value.into(),
315 description: None,
316 emoji: None,
317 default: None,
318 }
319 }
320
321 pub fn label(mut self, label: impl Into<String>) -> Self {
323 self.label = label.into();
324 self
325 }
326
327 pub fn value(mut self, value: impl Into<String>) -> Self {
329 self.value = value.into();
330 self
331 }
332
333 pub fn description(mut self, description: impl Into<String>) -> Self {
335 self.description = Some(description.into());
336 self
337 }
338
339 pub fn emoji(mut self, emoji: impl Into<ReactionType>) -> Self {
341 self.emoji = Some(emoji.into());
342 self
343 }
344
345 pub fn default_selection(mut self, default: bool) -> Self {
347 self.default = Some(default);
348 self
349 }
350}
351
352#[derive(Clone, Debug, Serialize, PartialEq)]
356#[must_use]
357pub struct CreateInputText(InputText);
358
359impl CreateInputText {
360 pub fn new(
363 style: InputTextStyle,
364 label: impl Into<String>,
365 custom_id: impl Into<String>,
366 ) -> Self {
367 Self(InputText {
368 style: Some(style),
369 label: Some(label.into()),
370 custom_id: custom_id.into(),
371
372 placeholder: None,
373 min_length: None,
374 max_length: None,
375 value: None,
376 required: true,
377
378 kind: ComponentType::InputText,
379 })
380 }
381
382 pub fn style(mut self, kind: InputTextStyle) -> Self {
384 self.0.style = Some(kind);
385 self
386 }
387
388 pub fn label(mut self, label: impl Into<String>) -> Self {
390 self.0.label = Some(label.into());
391 self
392 }
393
394 pub fn custom_id(mut self, id: impl Into<String>) -> Self {
397 self.0.custom_id = id.into();
398 self
399 }
400
401 pub fn placeholder(mut self, label: impl Into<String>) -> Self {
403 self.0.placeholder = Some(label.into());
404 self
405 }
406
407 pub fn min_length(mut self, min: u16) -> Self {
409 self.0.min_length = Some(min);
410 self
411 }
412
413 pub fn max_length(mut self, max: u16) -> Self {
415 self.0.max_length = Some(max);
416 self
417 }
418
419 pub fn value(mut self, value: impl Into<String>) -> Self {
421 self.0.value = Some(value.into());
422 self
423 }
424
425 pub fn required(mut self, required: bool) -> Self {
427 self.0.required = required;
428 self
429 }
430}