use serde::Serialize;
use crate::model::prelude::*;
#[derive(Clone, Debug, PartialEq)]
#[must_use]
pub enum CreateActionRow {
Buttons(Vec<CreateButton>),
SelectMenu(CreateSelectMenu),
InputText(CreateInputText),
}
impl serde::Serialize for CreateActionRow {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap as _;
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", &1_u8)?;
match self {
CreateActionRow::Buttons(buttons) => map.serialize_entry("components", &buttons)?,
CreateActionRow::SelectMenu(select) => map.serialize_entry("components", &[select])?,
CreateActionRow::InputText(input) => map.serialize_entry("components", &[input])?,
}
map.end()
}
}
#[derive(Clone, Debug, Serialize, PartialEq)]
#[must_use]
pub struct CreateButton(Button);
impl CreateButton {
pub fn new_link(url: impl Into<String>) -> Self {
Self(Button {
kind: ComponentType::Button,
data: ButtonKind::Link {
url: url.into(),
},
label: None,
emoji: None,
disabled: false,
})
}
pub fn new_premium(sku_id: impl Into<SkuId>) -> Self {
Self(Button {
kind: ComponentType::Button,
data: ButtonKind::Premium {
sku_id: sku_id.into(),
},
label: None,
emoji: None,
disabled: false,
})
}
pub fn new(custom_id: impl Into<String>) -> Self {
Self(Button {
kind: ComponentType::Button,
data: ButtonKind::NonLink {
style: ButtonStyle::Primary,
custom_id: custom_id.into(),
},
label: None,
emoji: None,
disabled: false,
})
}
pub fn custom_id(mut self, id: impl Into<String>) -> Self {
if let ButtonKind::NonLink {
custom_id, ..
} = &mut self.0.data
{
*custom_id = id.into();
}
self
}
pub fn style(mut self, new_style: ButtonStyle) -> Self {
if let ButtonKind::NonLink {
style, ..
} = &mut self.0.data
{
*style = new_style;
}
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.0.label = Some(label.into());
self
}
pub fn emoji(mut self, emoji: impl Into<ReactionType>) -> Self {
self.0.emoji = Some(emoji.into());
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.0.disabled = disabled;
self
}
}
impl From<Button> for CreateButton {
fn from(button: Button) -> Self {
Self(button)
}
}
struct CreateSelectMenuDefault(Mention);
impl Serialize for CreateSelectMenuDefault {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap as _;
let (id, kind) = match self.0 {
Mention::Channel(c) => (c.get(), "channel"),
Mention::Role(r) => (r.get(), "role"),
Mention::User(u) => (u.get(), "user"),
};
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("id", &id)?;
map.serialize_entry("type", kind)?;
map.end()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum CreateSelectMenuKind {
String { options: Vec<CreateSelectMenuOption> },
User { default_users: Option<Vec<UserId>> },
Role { default_roles: Option<Vec<RoleId>> },
Mentionable { default_users: Option<Vec<UserId>>, default_roles: Option<Vec<RoleId>> },
Channel { channel_types: Option<Vec<ChannelType>>, default_channels: Option<Vec<ChannelId>> },
}
impl Serialize for CreateSelectMenuKind {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[derive(Serialize)]
struct Json<'a> {
#[serde(rename = "type")]
kind: u8,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<&'a [CreateSelectMenuOption]>,
#[serde(skip_serializing_if = "Option::is_none")]
channel_types: Option<&'a [ChannelType]>,
#[serde(skip_serializing_if = "Vec::is_empty")]
default_values: Vec<CreateSelectMenuDefault>,
}
#[allow(clippy::ref_option)]
fn map<I: Into<Mention> + Copy>(
values: &Option<Vec<I>>,
) -> impl Iterator<Item = CreateSelectMenuDefault> + '_ {
values.iter().flatten().map(|&i| CreateSelectMenuDefault(i.into()))
}
#[rustfmt::skip]
let default_values = match self {
Self::String { .. } => vec![],
Self::User { default_users: default_values } => map(default_values).collect(),
Self::Role { default_roles: default_values } => map(default_values).collect(),
Self::Mentionable { default_users, default_roles } => {
let users = map(default_users);
let roles = map(default_roles);
users.chain(roles).collect()
},
Self::Channel { channel_types: _, default_channels: default_values } => map(default_values).collect(),
};
#[rustfmt::skip]
let json = Json {
kind: match self {
Self::String { .. } => 3,
Self::User { .. } => 5,
Self::Role { .. } => 6,
Self::Mentionable { .. } => 7,
Self::Channel { .. } => 8,
},
options: match self {
Self::String { options } => Some(options),
_ => None,
},
channel_types: match self {
Self::Channel { channel_types, default_channels: _ } => channel_types.as_deref(),
_ => None,
},
default_values,
};
json.serialize(serializer)
}
}
#[derive(Clone, Debug, Serialize, PartialEq)]
#[must_use]
pub struct CreateSelectMenu {
custom_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
placeholder: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
min_values: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
max_values: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
disabled: Option<bool>,
#[serde(flatten)]
kind: CreateSelectMenuKind,
}
impl CreateSelectMenu {
pub fn new(custom_id: impl Into<String>, kind: CreateSelectMenuKind) -> Self {
Self {
custom_id: custom_id.into(),
placeholder: None,
min_values: None,
max_values: None,
disabled: None,
kind,
}
}
pub fn placeholder(mut self, label: impl Into<String>) -> Self {
self.placeholder = Some(label.into());
self
}
pub fn custom_id(mut self, id: impl Into<String>) -> Self {
self.custom_id = id.into();
self
}
pub fn min_values(mut self, min: u8) -> Self {
self.min_values = Some(min);
self
}
pub fn max_values(mut self, max: u8) -> Self {
self.max_values = Some(max);
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = Some(disabled);
self
}
}
#[derive(Clone, Debug, Serialize, PartialEq)]
#[must_use]
pub struct CreateSelectMenuOption {
label: String,
value: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
emoji: Option<ReactionType>,
#[serde(skip_serializing_if = "Option::is_none")]
default: Option<bool>,
}
impl CreateSelectMenuOption {
pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
Self {
label: label.into(),
value: value.into(),
description: None,
emoji: None,
default: None,
}
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = label.into();
self
}
pub fn value(mut self, value: impl Into<String>) -> Self {
self.value = value.into();
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn emoji(mut self, emoji: impl Into<ReactionType>) -> Self {
self.emoji = Some(emoji.into());
self
}
pub fn default_selection(mut self, default: bool) -> Self {
self.default = Some(default);
self
}
}
#[derive(Clone, Debug, Serialize, PartialEq)]
#[must_use]
pub struct CreateInputText(InputText);
impl CreateInputText {
pub fn new(
style: InputTextStyle,
label: impl Into<String>,
custom_id: impl Into<String>,
) -> Self {
Self(InputText {
style: Some(style),
label: Some(label.into()),
custom_id: custom_id.into(),
placeholder: None,
min_length: None,
max_length: None,
value: None,
required: true,
kind: ComponentType::InputText,
})
}
pub fn style(mut self, kind: InputTextStyle) -> Self {
self.0.style = Some(kind);
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.0.label = Some(label.into());
self
}
pub fn custom_id(mut self, id: impl Into<String>) -> Self {
self.0.custom_id = id.into();
self
}
pub fn placeholder(mut self, label: impl Into<String>) -> Self {
self.0.placeholder = Some(label.into());
self
}
pub fn min_length(mut self, min: u16) -> Self {
self.0.min_length = Some(min);
self
}
pub fn max_length(mut self, max: u16) -> Self {
self.0.max_length = Some(max);
self
}
pub fn value(mut self, value: impl Into<String>) -> Self {
self.0.value = Some(value.into());
self
}
pub fn required(mut self, required: bool) -> Self {
self.0.required = required;
self
}
}