use std::fmt;
use crate::types::{
ActionRow, AllowedMentions, ButtonComponent, ButtonStyle, ChannelType, Checkbox, CheckboxGroup,
Component, Container, File, FileComponent, FileUpload, GroupOption, Label, MediaGallery,
MediaGalleryItem, MessageFlags, ModalCallbackData, PartialEmoji, RadioGroup, Section,
SelectDefaultValue, SelectMenu, SelectOption, Separator, SeparatorSpacing, Snowflake,
TextDisplay, TextInput, TextInputStyle, Thumbnail, UnfurledMedia,
};
use super::{
IntoAttr, IntoMedia, IntoText, IntoView, ModalChoice, ModalView, View, custom_id::CustomIdError,
};
#[doc(hidden)]
#[derive(Debug, Clone, Default)]
pub struct Extras(Option<Box<ExtrasInner>>);
#[derive(Debug, Clone)]
pub(crate) struct ExtrasInner {
pub(crate) files: Vec<File>,
pub(crate) flags: MessageFlags,
pub(crate) allowed_mentions: Option<AllowedMentions>,
pub(crate) error: Option<CustomIdError>,
}
impl Default for ExtrasInner {
fn default() -> Self {
Self {
files: Vec::new(),
flags: MessageFlags::empty(),
allowed_mentions: None,
error: None,
}
}
}
impl Extras {
pub(crate) fn inner(&mut self) -> &mut ExtrasInner {
self.0.get_or_insert_with(Default::default)
}
pub(crate) fn into_inner(self) -> ExtrasInner {
self.0.map(|inner| *inner).unwrap_or_default()
}
pub(crate) fn files(&self) -> &[File] {
self.0.as_ref().map_or(&[], |inner| &inner.files)
}
pub(crate) fn flags(&self) -> MessageFlags {
self.0
.as_ref()
.map_or(MessageFlags::empty(), |inner| inner.flags)
}
pub(crate) fn mentions(&self) -> Option<&AllowedMentions> {
self.0
.as_ref()
.and_then(|inner| inner.allowed_mentions.as_ref())
}
pub(crate) fn custom_id_error(&self) -> Option<&CustomIdError> {
self.0.as_ref().and_then(|inner| inner.error.as_ref())
}
pub(crate) fn merge(&mut self, other: Extras) {
let Some(other) = other.0 else {
return;
};
let Some(this) = &mut self.0 else {
self.0 = Some(other);
return;
};
let other = *other;
if this.error.is_none() {
this.error = other.error;
}
this.files.extend(other.files);
this.flags |= other.flags;
if this.allowed_mentions.is_none() {
this.allowed_mentions = other.allowed_mentions;
}
}
pub(crate) fn error(&mut self, error: CustomIdError) {
self.inner().error.get_or_insert(error);
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct Element<T> {
pub(crate) value: T,
pub(crate) extras: Extras,
}
impl<T> Element<T> {
fn new(value: T, extras: Extras) -> Self {
Self { value, extras }
}
pub fn into_inner(self) -> T {
self.value
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a custom_id",
note = "use a String, a &str, or a handler's `custom_id(..)`"
)]
pub trait IntoCustomId {
fn into_custom_id(self, extras: &mut Extras) -> String;
}
impl IntoCustomId for String {
fn into_custom_id(self, _extras: &mut Extras) -> String {
self
}
}
impl IntoCustomId for &str {
fn into_custom_id(self, _extras: &mut Extras) -> String {
self.to_string()
}
}
impl IntoCustomId for &String {
fn into_custom_id(self, _extras: &mut Extras) -> String {
self.clone()
}
}
impl IntoCustomId for Result<String, CustomIdError> {
fn into_custom_id(self, extras: &mut Extras) -> String {
self.unwrap_or_else(|error| {
extras.error(error);
String::new()
})
}
}
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct Kids {
components: Vec<Component>,
text: Option<String>,
extras: Extras,
}
impl Kids {
fn reserve(&mut self, additional: usize) {
self.components.reserve(additional);
}
fn text(&mut self, text: impl IntoText) {
text.push_text(self.text.get_or_insert_with(String::new));
}
fn component(&mut self, component: Component) {
self.flush();
self.components.push(component);
}
fn element<T: Into<Component>>(&mut self, element: Element<T>) {
self.extras.merge(element.extras);
self.component(element.value.into());
}
fn view(&mut self, view: View) {
self.extras.merge(view.extras);
for component in view.components {
self.component(component);
}
}
fn flush(&mut self) {
if let Some(text) = self.text.take() {
self.components.push(Component::text(text));
}
}
fn finish(mut self) -> (Vec<Component>, Extras) {
self.flush();
(self.components, self.extras)
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct List<T> {
items: Vec<T>,
extras: Extras,
}
impl<T> Default for List<T> {
fn default() -> Self {
Self {
items: Vec::new(),
extras: Extras::default(),
}
}
}
macro_rules! collections {
($trait:ident, $target:ty) => {
impl<T: $trait> $trait for Option<T> {
fn add_to(self, parent: &mut $target) {
if let Some(child) = self {
child.add_to(parent);
}
}
}
impl<T: $trait> $trait for Vec<T> {
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
impl<T: $trait, const N: usize> $trait for [T; N] {
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
impl<T: $trait> $trait for ::std::vec::IntoIter<T> {
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
collections!(@iter $trait, $target, Map<I, F>, Filter<I, P>, FilterMap<I, F>, Chain<A, B>,
Take<I>, Skip<I>, Rev<I>, Once<T>);
impl<I: Iterator, U: IntoIterator<Item: $trait>, F: FnMut(I::Item) -> U> $trait
for ::core::iter::FlatMap<I, U, F>
{
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
impl<I: Iterator<Item: IntoIterator<Item: $trait>>> $trait for ::core::iter::Flatten<I> {
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
};
(@iter $trait:ident, $target:ty, $($ty:ident<$($param:ident),*>),*) => {$(
impl<$($param),*> $trait for ::core::iter::$ty<$($param),*>
where
::core::iter::$ty<$($param),*>: Iterator<Item: $trait>,
{
fn add_to(self, parent: &mut $target) {
for child in self {
child.add_to(parent);
}
}
}
)*};
}
macro_rules! child_trait {
($(#[$doc:meta])* $trait:ident, $target:ty, $message:literal, $note:literal) => {
$(#[$doc])*
#[diagnostic::on_unimplemented(message = $message, note = $note)]
pub trait $trait {
#[doc(hidden)]
fn add_to(self, parent: &mut $target);
}
collections!($trait, $target);
};
}
child_trait!(
MessageChild,
Kids,
"`{Self}` cannot go at the top of a message",
"use text, a <Text>, <Container>, <Section>, <Gallery>, <File>, <Separator> or <Row>, a Component, a View or a #[view]'s Props, or an Option, Vec or iterator of them"
);
child_trait!(
ContainerChild,
Kids,
"`{Self}` cannot go in a `<Container>`",
"a <Container> takes text, <Text>, <Section>, <Gallery>, <File>, <Separator> and <Row>, components, Views and Props; buttons and selects go in a <Row>"
);
child_trait!(
SectionChild,
SectionBuilder,
"`{Self}` cannot go in a `<Section>`",
"a <Section> takes 1-3 texts or <Text>s, and one <Button> or <Thumbnail> as its accessory"
);
child_trait!(
RowChild,
Kids,
"`{Self}` cannot go in a `<Row>`",
"a <Row> takes up to 5 <Button>s, or one select"
);
child_trait!(
ModalChild,
Kids,
"`{Self}` cannot go in a `<Modal>`",
"a <Modal> takes text, <Text> and <Label>s (each around one input)"
);
child_trait!(
LabelChild,
LabelBuilder,
"`{Self}` cannot go in a `<Label>`",
"a <Label> holds one input: <TextInput>, a select, <RadioGroup>, <CheckboxGroup>, <Checkbox> or <FileUpload>"
);
child_trait!(
GalleryChild,
List<MediaGalleryItem>,
"`{Self}` cannot go in a `<Gallery>`",
"a <Gallery> takes <Item>s and MediaGalleryItems"
);
child_trait!(
SelectOptionChild,
List<SelectOption>,
"`{Self}` cannot go in a select",
"a select takes <Option>s and SelectOptions"
);
child_trait!(
GroupOptionChild,
List<GroupOption>,
"`{Self}` cannot go in a group",
"a <RadioGroup> or <CheckboxGroup> takes <Option>s and GroupOptions"
);
macro_rules! text_child {
($($ty:ty),*) => {$(
impl MessageChild for $ty {
fn add_to(self, parent: &mut Kids) {
parent.text(self);
}
}
impl ContainerChild for $ty {
fn add_to(self, parent: &mut Kids) {
parent.text(self);
}
}
impl SectionChild for $ty {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.text(self);
}
}
impl ModalChild for $ty {
fn add_to(self, parent: &mut Kids) {
parent.text(self);
}
}
)*};
}
text_child!(
&str,
String,
&String,
char,
u8,
u16,
u32,
u64,
u128,
usize,
i8,
i16,
i32,
i64,
i128,
isize,
f32,
f64,
Snowflake,
fmt::Arguments<'_>
);
macro_rules! view_child {
($($trait:ident),*) => {$(
#[diagnostic::do_not_recommend]
impl<V: IntoView> $trait for V {
fn add_to(self, parent: &mut Kids) {
parent.view(self.into_view());
}
}
)*};
}
view_child!(MessageChild, ContainerChild, RowChild, ModalChild);
#[diagnostic::do_not_recommend]
impl<V: IntoView> SectionChild for V {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.view(self.into_view());
}
}
macro_rules! component_child {
($($trait:ident),*) => {$(
impl $trait for Component {
fn add_to(self, parent: &mut Kids) {
parent.component(self);
}
}
)*};
}
component_child!(MessageChild, ContainerChild, RowChild, ModalChild);
impl SectionChild for Component {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.component(self);
}
}
impl LabelChild for Component {
fn add_to(self, parent: &mut LabelBuilder) {
parent.input = Some(self);
}
}
macro_rules! placed {
($ty:ty => $($trait:ident),*) => {$(
impl $trait for Element<$ty> {
fn add_to(self, parent: &mut Kids) {
parent.element(self);
}
}
impl $trait for $ty {
fn add_to(self, parent: &mut Kids) {
parent.component(self.into());
}
}
)*};
}
placed!(TextDisplay => MessageChild, ContainerChild, ModalChild);
placed!(Container => MessageChild);
placed!(Section => MessageChild, ContainerChild);
placed!(MediaGallery => MessageChild, ContainerChild);
placed!(FileComponent => MessageChild, ContainerChild);
placed!(Separator => MessageChild, ContainerChild);
placed!(ActionRow => MessageChild, ContainerChild);
placed!(ButtonComponent => MessageChild, RowChild);
placed!(Label => ModalChild);
macro_rules! select_placed {
($($n:literal),*) => {$(
placed!(SelectMenu<$n> => MessageChild, RowChild);
impl LabelChild for Element<SelectMenu<$n>> {
fn add_to(self, parent: &mut LabelBuilder) {
parent.extras.merge(self.extras);
parent.input = Some(self.value.into());
}
}
impl LabelChild for SelectMenu<$n> {
fn add_to(self, parent: &mut LabelBuilder) {
parent.input = Some(self.into());
}
}
)*};
}
select_placed!(3, 5, 6, 7, 8);
impl SectionChild for Element<TextDisplay> {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.element(self);
}
}
impl SectionChild for TextDisplay {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.component(self.into());
}
}
macro_rules! accessory {
($($ty:ty),*) => {$(
impl SectionChild for Element<$ty> {
fn add_to(self, parent: &mut SectionBuilder) {
parent.kids.extras.merge(self.extras);
parent.accessory = Some(self.value.into());
}
}
impl SectionChild for $ty {
fn add_to(self, parent: &mut SectionBuilder) {
parent.accessory = Some(self.into());
}
}
)*};
}
accessory!(ButtonComponent, Thumbnail);
macro_rules! input {
($($ty:ty),*) => {$(
impl LabelChild for Element<$ty> {
fn add_to(self, parent: &mut LabelBuilder) {
parent.extras.merge(self.extras);
parent.input = Some(self.value.into());
}
}
impl LabelChild for $ty {
fn add_to(self, parent: &mut LabelBuilder) {
parent.input = Some(self.into());
}
}
)*};
}
input!(TextInput, RadioGroup, CheckboxGroup, Checkbox, FileUpload);
impl GalleryChild for Element<MediaGalleryItem> {
fn add_to(self, parent: &mut List<MediaGalleryItem>) {
parent.extras.merge(self.extras);
parent.items.push(self.value);
}
}
impl GalleryChild for MediaGalleryItem {
fn add_to(self, parent: &mut List<MediaGalleryItem>) {
parent.items.push(self);
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct ChoiceOption {
label: String,
value: String,
description: Option<String>,
emoji: Option<PartialEmoji>,
default: Option<bool>,
}
impl SelectOptionChild for Element<ChoiceOption> {
fn add_to(self, parent: &mut List<SelectOption>) {
let o = self.value;
parent.items.push(SelectOption::new(
o.label,
o.value,
o.description,
o.emoji,
o.default,
));
}
}
impl SelectOptionChild for SelectOption {
fn add_to(self, parent: &mut List<SelectOption>) {
parent.items.push(self);
}
}
impl GroupOptionChild for Element<ChoiceOption> {
fn add_to(self, parent: &mut List<GroupOption>) {
let o = self.value;
let mut option = GroupOption::new(o.label, o.value);
option.description = o.description;
option.default = o.default;
parent.items.push(option);
}
}
impl GroupOptionChild for GroupOption {
fn add_to(self, parent: &mut List<GroupOption>) {
parent.items.push(self);
}
}
macro_rules! setters {
($($(#[$doc:meta])* $name:ident: $ty:ty => $($field:ident).+;)*) => {$(
$(#[$doc])*
pub fn $name(mut self, value: impl IntoAttr<$ty>) -> Self {
self.$($field).+ = value.into_attr();
self
}
)*};
}
macro_rules! reserve {
($($field:ident).+) => {
#[doc(hidden)]
pub fn reserve(mut self, additional: usize) -> Self {
self.$($field).+.reserve(additional);
self
}
};
}
#[derive(Debug, Default)]
#[must_use]
pub struct ViewBuilder {
kids: Kids,
}
impl View {
pub fn builder() -> ViewBuilder {
ViewBuilder::default()
}
}
impl ViewBuilder {
reserve!(kids);
pub fn child(mut self, child: impl MessageChild) -> Self {
child.add_to(&mut self.kids);
self
}
pub fn build(self) -> View {
let (components, extras) = self.kids.finish();
View { components, extras }
}
}
#[derive(Debug)]
#[must_use]
pub struct ContainerBuilder {
el: Container,
kids: Kids,
}
impl Container {
pub fn builder() -> ContainerBuilder {
ContainerBuilder {
el: Container::new(Vec::new()),
kids: Kids::default(),
}
}
}
impl ContainerBuilder {
setters! {
id: Option<i32> => el.id;
accent_color: Option<u32> => el.accent_color;
spoiler: Option<bool> => el.spoiler;
}
reserve!(kids);
pub fn child(mut self, child: impl ContainerChild) -> Self {
child.add_to(&mut self.kids);
self
}
pub fn build(mut self) -> Element<Container> {
let (components, extras) = self.kids.finish();
self.el.components = components;
Element::new(self.el, extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct SectionBuilder {
id: Option<i32>,
kids: Kids,
accessory: Option<Component>,
}
impl Section {
pub fn builder() -> SectionBuilder {
SectionBuilder {
id: None,
kids: Kids::default(),
accessory: None,
}
}
}
impl SectionBuilder {
setters! {
id: Option<i32> => id;
}
reserve!(kids);
pub fn accessory(mut self, accessory: impl Into<Component>) -> Self {
self.accessory = Some(accessory.into());
self
}
pub fn child(mut self, child: impl SectionChild) -> Self {
child.add_to(&mut self);
self
}
pub fn build(self) -> Element<Section> {
let (components, extras) = self.kids.finish();
let accessory = self
.accessory
.unwrap_or_else(|| Component::Separator(Separator::new()));
let mut section = Section::new(components, accessory);
section.id = self.id;
Element::new(section, extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct TextBuilder {
el: TextDisplay,
text: Option<String>,
}
impl TextDisplay {
pub fn builder() -> TextBuilder {
TextBuilder {
el: TextDisplay::new(String::new()),
text: None,
}
}
}
impl TextBuilder {
setters! {
id: Option<i32> => el.id;
content: String => el.content;
}
#[doc(hidden)]
pub fn reserve(mut self, additional: usize) -> Self {
self.text
.get_or_insert_with(String::new)
.reserve(additional);
self
}
pub fn child(mut self, text: impl IntoText) -> Self {
text.push_text(self.text.get_or_insert_with(String::new));
self
}
pub fn build(mut self) -> Element<TextDisplay> {
if let Some(text) = self.text {
self.el.content = text;
}
Element::new(self.el, Extras::default())
}
}
macro_rules! media {
($($field:ident).+) => {
pub fn src(mut self, media: impl IntoMedia) -> Self {
self.$($field).+ = UnfurledMedia::new(media.into_media(&mut self.extras));
self
}
pub fn url(self, media: impl IntoMedia) -> Self {
self.src(media)
}
};
}
#[derive(Debug)]
#[must_use]
pub struct ThumbnailBuilder {
el: Thumbnail,
extras: Extras,
}
impl Thumbnail {
pub fn builder() -> ThumbnailBuilder {
ThumbnailBuilder {
el: Thumbnail::new(String::new()),
extras: Extras::default(),
}
}
}
impl ThumbnailBuilder {
setters! {
id: Option<i32> => el.id;
description: Option<String> => el.description;
spoiler: Option<bool> => el.spoiler;
}
media!(el.media);
pub fn build(self) -> Element<Thumbnail> {
Element::new(self.el, self.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct GalleryBuilder {
el: MediaGallery,
items: List<MediaGalleryItem>,
}
impl MediaGallery {
pub fn builder() -> GalleryBuilder {
GalleryBuilder {
el: MediaGallery::new(Vec::new()),
items: List::default(),
}
}
}
impl GalleryBuilder {
setters! {
id: Option<i32> => el.id;
}
reserve!(items.items);
pub fn child(mut self, item: impl GalleryChild) -> Self {
item.add_to(&mut self.items);
self
}
pub fn build(mut self) -> Element<MediaGallery> {
self.el.items = self.items.items;
Element::new(self.el, self.items.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct ItemBuilder {
el: MediaGalleryItem,
extras: Extras,
}
impl MediaGalleryItem {
pub fn builder() -> ItemBuilder {
ItemBuilder {
el: MediaGalleryItem::new(String::new()),
extras: Extras::default(),
}
}
}
impl ItemBuilder {
setters! {
description: Option<String> => el.description;
spoiler: Option<bool> => el.spoiler;
}
media!(el.media);
pub fn build(self) -> Element<MediaGalleryItem> {
Element::new(self.el, self.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct FileBuilder {
el: FileComponent,
extras: Extras,
}
impl FileComponent {
pub fn builder() -> FileBuilder {
FileBuilder {
el: FileComponent::new(String::new()),
extras: Extras::default(),
}
}
}
impl FileBuilder {
setters! {
id: Option<i32> => el.id;
spoiler: Option<bool> => el.spoiler;
}
media!(el.file);
pub fn build(self) -> Element<FileComponent> {
Element::new(self.el, self.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct SeparatorBuilder {
el: Separator,
}
impl Separator {
pub fn builder() -> SeparatorBuilder {
SeparatorBuilder {
el: Separator::new(),
}
}
}
impl SeparatorBuilder {
setters! {
id: Option<i32> => el.id;
spacing: Option<SeparatorSpacing> => el.spacing;
divider: Option<bool> => el.divider;
}
pub fn small(mut self, small: bool) -> Self {
if small {
self.el.spacing = Some(SeparatorSpacing::Small);
}
self
}
pub fn large(mut self, large: bool) -> Self {
if large {
self.el.spacing = Some(SeparatorSpacing::Large);
}
self
}
pub fn build(self) -> Element<Separator> {
Element::new(self.el, Extras::default())
}
}
#[derive(Debug)]
#[must_use]
pub struct RowBuilder {
el: ActionRow,
kids: Kids,
}
impl ActionRow {
pub fn builder() -> RowBuilder {
RowBuilder {
el: ActionRow::new(Vec::new()),
kids: Kids::default(),
}
}
}
impl RowBuilder {
setters! {
id: Option<i32> => el.id;
}
reserve!(kids);
pub fn child(mut self, child: impl RowChild) -> Self {
child.add_to(&mut self.kids);
self
}
pub fn build(mut self) -> Element<ActionRow> {
let (components, extras) = self.kids.finish();
self.el.components = components;
Element::new(self.el, extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct ButtonBuilder {
el: ButtonComponent,
style: Option<ButtonStyle>,
label: Option<String>,
extras: Extras,
}
impl ButtonComponent {
pub fn builder() -> ButtonBuilder {
ButtonBuilder {
el: ButtonComponent::new(ButtonStyle::Secondary, None, None, None, None, None),
style: None,
label: None,
extras: Extras::default(),
}
}
}
impl ButtonBuilder {
setters! {
id: Option<i32> => el.id;
url: Option<String> => el.url;
sku_id: Option<Snowflake> => el.sku_id;
label: Option<String> => el.label;
emoji: Option<PartialEmoji> => el.emoji;
disabled: Option<bool> => el.disabled;
}
fn flag(mut self, on: bool, style: ButtonStyle) -> Self {
if on {
self.style = Some(style);
}
self
}
pub fn primary(self, on: bool) -> Self {
self.flag(on, ButtonStyle::Primary)
}
pub fn secondary(self, on: bool) -> Self {
self.flag(on, ButtonStyle::Secondary)
}
pub fn success(self, on: bool) -> Self {
self.flag(on, ButtonStyle::Success)
}
pub fn danger(self, on: bool) -> Self {
self.flag(on, ButtonStyle::Danger)
}
pub fn style(mut self, style: impl IntoAttr<ButtonStyle>) -> Self {
self.style = Some(style.into_attr());
self
}
pub fn on_click(self, custom_id: impl IntoCustomId) -> Self {
self.custom_id(custom_id)
}
pub fn custom_id(mut self, custom_id: impl IntoCustomId) -> Self {
self.el.custom_id = Some(custom_id.into_custom_id(&mut self.extras));
self
}
pub fn child(mut self, text: impl IntoText) -> Self {
text.push_text(self.label.get_or_insert_with(String::new));
self
}
pub fn build(mut self) -> Element<ButtonComponent> {
self.el.style = match self.style {
Some(style) => style,
None if self.el.url.is_some() => ButtonStyle::Link,
None if self.el.sku_id.is_some() => ButtonStyle::Premium,
None => ButtonStyle::Secondary,
};
if self.label.is_some() {
self.el.label = self.label;
}
Element::new(self.el, self.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct SelectBuilder<const T: u8> {
el: SelectMenu<T>,
options: List<SelectOption>,
many: bool,
}
impl<const T: u8> SelectMenu<T> {
pub fn builder() -> SelectBuilder<T> {
SelectBuilder {
el: SelectMenu::new(String::new(), None, None, None, None, None, None),
options: List::default(),
many: false,
}
}
}
impl<const T: u8> SelectBuilder<T> {
setters! {
id: Option<i32> => el.id;
placeholder: Option<String> => el.placeholder;
min: Option<i32> => el.min_values;
min_values: Option<i32> => el.min_values;
max: Option<i32> => el.max_values;
max_values: Option<i32> => el.max_values;
disabled: Option<bool> => el.disabled;
required: Option<bool> => el.required;
}
pub fn on_select(self, custom_id: impl IntoCustomId) -> Self {
self.custom_id(custom_id)
}
pub fn custom_id(mut self, custom_id: impl IntoCustomId) -> Self {
self.el.custom_id = custom_id.into_custom_id(&mut self.options.extras);
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.el.custom_id = name.into();
self
}
#[doc(hidden)]
pub fn many(mut self, many: bool) -> Self {
self.many = many;
self
}
#[doc(hidden)]
pub fn required_default(mut self, required: Option<bool>) -> Self {
if self.el.required.is_none() {
self.el.required = required;
}
self
}
pub fn build(mut self) -> Element<SelectMenu<T>> {
if T == 3 {
self.el.options = Some(self.options.items);
}
if self.many && self.el.max_values.is_none() {
let all = self.el.options.as_ref().map_or(25, Vec::len).min(25);
self.el.max_values = Some(all as i32);
}
Element::new(self.el, self.options.extras)
}
}
impl SelectBuilder<3> {
reserve!(options.items);
pub fn options<C: ModalChoice>(mut self) -> Self {
self.options
.items
.extend(C::OPTIONS.iter().map(|(label, value)| {
SelectOption::new(label.to_string(), value.to_string(), None, None, None)
}));
self
}
pub fn child(mut self, option: impl SelectOptionChild) -> Self {
option.add_to(&mut self.options);
self
}
}
macro_rules! entity_select {
($($n:literal),*) => {$(
impl SelectBuilder<$n> {
setters! {
default_values: Option<Vec<SelectDefaultValue>> => el.default_values;
}
}
)*};
}
entity_select!(5, 6, 7, 8);
impl SelectBuilder<8> {
setters! {
channel_types: Option<Vec<ChannelType>> => el.channel_types;
}
}
#[derive(Debug)]
#[must_use]
pub struct OptionBuilder {
option: ChoiceOption,
label: Option<String>,
}
impl SelectOption {
pub fn builder() -> OptionBuilder {
OptionBuilder {
option: ChoiceOption {
label: String::new(),
value: String::new(),
description: None,
emoji: None,
default: None,
},
label: None,
}
}
}
impl OptionBuilder {
setters! {
value: String => option.value;
label: String => option.label;
description: Option<String> => option.description;
emoji: Option<PartialEmoji> => option.emoji;
default: Option<bool> => option.default;
}
pub fn child(mut self, text: impl IntoText) -> Self {
text.push_text(self.label.get_or_insert_with(String::new));
self
}
pub fn build(mut self) -> Element<ChoiceOption> {
if let Some(label) = self.label {
self.option.label = label;
}
Element::new(self.option, Extras::default())
}
}
#[derive(Debug)]
#[must_use]
pub struct ModalBuilder {
data: ModalCallbackData,
kids: Kids,
}
impl ModalView {
pub fn builder() -> ModalBuilder {
ModalBuilder {
data: ModalCallbackData {
custom_id: String::new(),
title: String::new(),
components: None,
tts: None,
content: None,
embeds: None,
allowed_mentions: None,
flags: None,
},
kids: Kids::default(),
}
}
}
impl ModalBuilder {
setters! {
title: String => data.title;
}
reserve!(kids);
pub fn on_submit(self, custom_id: impl IntoCustomId) -> Self {
self.custom_id(custom_id)
}
pub fn custom_id(mut self, custom_id: impl IntoCustomId) -> Self {
self.data.custom_id = custom_id.into_custom_id(&mut self.kids.extras);
self
}
pub fn child(mut self, child: impl ModalChild) -> Self {
child.add_to(&mut self.kids);
self
}
pub fn build(mut self) -> ModalView {
let (components, extras) = self.kids.finish();
self.data.components = Some(components);
ModalView {
data: self.data,
error: extras.into_inner().error,
}
}
}
#[derive(Debug)]
#[must_use]
pub struct LabelBuilder {
id: Option<i32>,
text: String,
description: Option<String>,
input: Option<Component>,
extras: Extras,
}
impl Label {
pub fn builder() -> LabelBuilder {
LabelBuilder {
id: None,
text: String::new(),
description: None,
input: None,
extras: Extras::default(),
}
}
}
impl LabelBuilder {
setters! {
id: Option<i32> => id;
text: String => text;
label: String => text;
description: Option<String> => description;
}
pub fn child(mut self, input: impl LabelChild) -> Self {
input.add_to(&mut self);
self
}
pub fn build(self) -> Element<Label> {
let input = self
.input
.unwrap_or_else(|| Component::Separator(Separator::new()));
let mut label = Label::new(self.text, input);
label.id = self.id;
label.description = self.description;
Element::new(label, self.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct TextInputBuilder {
el: TextInput,
}
impl TextInput {
pub fn builder() -> TextInputBuilder {
let mut el = TextInput::new(
String::new(),
TextInputStyle::Short,
String::new(),
None,
None,
None,
None,
None,
);
el.label = None;
TextInputBuilder { el }
}
}
macro_rules! input_name {
() => {
pub fn name(mut self, name: impl Into<String>) -> Self {
self.el.custom_id = name.into();
self
}
#[doc(hidden)]
pub fn required_default(mut self, required: Option<bool>) -> Self {
if self.el.required.is_none() {
self.el.required = required;
}
self
}
};
}
impl TextInputBuilder {
setters! {
id: Option<i32> => el.id;
style: TextInputStyle => el.style;
min_len: Option<i32> => el.min_length;
min_length: Option<i32> => el.min_length;
max_len: Option<i32> => el.max_length;
max_length: Option<i32> => el.max_length;
placeholder: Option<String> => el.placeholder;
value: Option<String> => el.value;
required: Option<bool> => el.required;
}
input_name!();
pub fn paragraph(mut self, paragraph: bool) -> Self {
if paragraph {
self.el.style = TextInputStyle::Paragraph;
}
self
}
pub fn build(self) -> Element<TextInput> {
Element::new(self.el, Extras::default())
}
}
#[derive(Debug)]
#[must_use]
pub struct RadioGroupBuilder {
el: RadioGroup,
options: List<GroupOption>,
}
impl RadioGroup {
pub fn builder() -> RadioGroupBuilder {
RadioGroupBuilder {
el: RadioGroup::new(String::new(), Vec::new()),
options: List::default(),
}
}
}
macro_rules! group_options {
() => {
reserve!(options.items);
pub fn options<C: ModalChoice>(mut self) -> Self {
self.options.items.extend(
C::OPTIONS
.iter()
.map(|(label, value)| GroupOption::new(*label, *value)),
);
self
}
pub fn child(mut self, option: impl GroupOptionChild) -> Self {
option.add_to(&mut self.options);
self
}
};
}
impl RadioGroupBuilder {
setters! {
id: Option<i32> => el.id;
required: Option<bool> => el.required;
}
input_name!();
group_options!();
pub fn build(mut self) -> Element<RadioGroup> {
self.el.options = self.options.items;
Element::new(self.el, self.options.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct CheckboxGroupBuilder {
el: CheckboxGroup,
options: List<GroupOption>,
}
impl CheckboxGroup {
pub fn builder() -> CheckboxGroupBuilder {
CheckboxGroupBuilder {
el: CheckboxGroup::new(String::new(), Vec::new()),
options: List::default(),
}
}
}
impl CheckboxGroupBuilder {
setters! {
id: Option<i32> => el.id;
min: Option<u8> => el.min_values;
min_values: Option<u8> => el.min_values;
max: Option<u8> => el.max_values;
max_values: Option<u8> => el.max_values;
required: Option<bool> => el.required;
}
input_name!();
group_options!();
pub fn build(mut self) -> Element<CheckboxGroup> {
self.el.options = self.options.items;
Element::new(self.el, self.options.extras)
}
}
#[derive(Debug)]
#[must_use]
pub struct CheckboxBuilder {
el: Checkbox,
}
impl Checkbox {
pub fn builder() -> CheckboxBuilder {
CheckboxBuilder {
el: Checkbox::new(String::new()),
}
}
}
impl CheckboxBuilder {
setters! {
id: Option<i32> => el.id;
default: Option<bool> => el.default;
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.el.custom_id = name.into();
self
}
pub fn build(self) -> Element<Checkbox> {
Element::new(self.el, Extras::default())
}
}
#[derive(Debug)]
#[must_use]
pub struct FileUploadBuilder {
el: FileUpload,
many: bool,
}
impl FileUpload {
pub fn builder() -> FileUploadBuilder {
FileUploadBuilder {
el: FileUpload::new(String::new()),
many: false,
}
}
}
impl FileUploadBuilder {
setters! {
id: Option<i32> => el.id;
min: Option<u8> => el.min_values;
min_values: Option<u8> => el.min_values;
max: Option<u8> => el.max_values;
max_values: Option<u8> => el.max_values;
file_types: Option<Vec<String>> => el.file_types;
required: Option<bool> => el.required;
}
input_name!();
#[doc(hidden)]
pub fn many(mut self, many: bool) -> Self {
self.many = many;
self
}
pub fn build(mut self) -> Element<FileUpload> {
if self.many && self.el.max_values.is_none() {
self.el.max_values = Some(10);
}
Element::new(self.el, Extras::default())
}
}
#[doc(hidden)]
pub mod elements {
pub use super::super::ModalView as Modal;
pub use crate::types::{
ActionRow, ActionRow as Row, ButtonComponent as Button, ChannelSelect, Checkbox,
CheckboxGroup, Container, FileComponent as File, FileUpload, Label, MediaGallery,
MediaGallery as Gallery, MediaGalleryItem, MediaGalleryItem as Item, MentionableSelect,
RadioGroup, RoleSelect, Section, SelectOption as Option, Separator, StringSelect,
StringSelect as Select, TextDisplay, TextDisplay as Text, TextInput, Thumbnail, UserSelect,
};
}