diff --git a/examples/e14_slash_commands/Cargo.toml b/examples/e14_slash_commands/Cargo.toml index 4e73adeb7f2..d586c584823 100644 --- a/examples/e14_slash_commands/Cargo.toml +++ b/examples/e14_slash_commands/Cargo.toml @@ -2,7 +2,7 @@ name = "e14_slash_commands" version = "0.1.0" authors = ["my name "] -edition = "2018" +edition = "2021" [dependencies] serenity = { path = "../../", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "collector"] } diff --git a/examples/e14_slash_commands/src/commands/attachmentinput.rs b/examples/e14_slash_commands/src/commands/attachmentinput.rs index 21924fe2027..1dce65a1c14 100644 --- a/examples/e14_slash_commands/src/commands/attachmentinput.rs +++ b/examples/e14_slash_commands/src/commands/attachmentinput.rs @@ -12,7 +12,7 @@ pub fn run(options: &[ResolvedOption]) -> String { } } -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("attachmentinput") .description("Test command for attachment input") .add_option( diff --git a/examples/e14_slash_commands/src/commands/id.rs b/examples/e14_slash_commands/src/commands/id.rs index 74e29911191..47da776fb08 100644 --- a/examples/e14_slash_commands/src/commands/id.rs +++ b/examples/e14_slash_commands/src/commands/id.rs @@ -12,7 +12,7 @@ pub fn run(options: &[ResolvedOption]) -> String { } } -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("id").description("Get a user id").add_option( CreateCommandOption::new(CommandOptionType::User, "id", "The user to lookup") .required(true), diff --git a/examples/e14_slash_commands/src/commands/modal.rs b/examples/e14_slash_commands/src/commands/modal.rs index 1f3e7f918a1..1874ac6e80b 100644 --- a/examples/e14_slash_commands/src/commands/modal.rs +++ b/examples/e14_slash_commands/src/commands/modal.rs @@ -26,6 +26,6 @@ pub async fn run(ctx: &Context, interaction: &CommandInteraction) -> Result<(), Ok(()) } -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("modal").description("Asks some details about you") } diff --git a/examples/e14_slash_commands/src/commands/numberinput.rs b/examples/e14_slash_commands/src/commands/numberinput.rs index f7643bd3cca..18b77edf27a 100644 --- a/examples/e14_slash_commands/src/commands/numberinput.rs +++ b/examples/e14_slash_commands/src/commands/numberinput.rs @@ -1,7 +1,7 @@ use serenity::builder::{CreateCommand, CreateCommandOption}; use serenity::model::application::CommandOptionType; -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("numberinput") .description("Test command for number input") .add_option( diff --git a/examples/e14_slash_commands/src/commands/ping.rs b/examples/e14_slash_commands/src/commands/ping.rs index cd92b879919..6970a84e4fc 100644 --- a/examples/e14_slash_commands/src/commands/ping.rs +++ b/examples/e14_slash_commands/src/commands/ping.rs @@ -5,6 +5,6 @@ pub fn run(_options: &[ResolvedOption]) -> String { "Hey, I'm alive!".to_string() } -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("ping").description("A ping command") } diff --git a/examples/e14_slash_commands/src/commands/welcome.rs b/examples/e14_slash_commands/src/commands/welcome.rs index e11a98d6c7b..08d3bd86f61 100644 --- a/examples/e14_slash_commands/src/commands/welcome.rs +++ b/examples/e14_slash_commands/src/commands/welcome.rs @@ -1,7 +1,16 @@ +use std::borrow::Cow; +use std::collections::HashMap; + use serenity::builder::{CreateCommand, CreateCommandOption}; use serenity::model::application::CommandOptionType; -pub fn register() -> CreateCommand { +fn new_map<'a>(key: &'a str, value: &'a str) -> HashMap, Cow<'a, str>> { + let mut map = HashMap::with_capacity(1); + map.insert(Cow::Borrowed(key), Cow::Borrowed(value)); + map +} + +pub fn register() -> CreateCommand<'static> { CreateCommand::new("welcome") .description("Welcome a user") .name_localized("de", "begrüßen") @@ -20,27 +29,28 @@ pub fn register() -> CreateCommand { .add_string_choice_localized( "Welcome to our cool server! Ask me if you need help", "pizza", - [( + new_map( "de", "Willkommen auf unserem coolen Server! Frag mich, falls du Hilfe brauchst", - )], + ), + ) + .add_string_choice_localized( + "Hey, do you want a coffee?", + "coffee", + new_map("de", "Hey, willst du einen Kaffee?"), ) - .add_string_choice_localized("Hey, do you want a coffee?", "coffee", [( - "de", - "Hey, willst du einen Kaffee?", - )]) .add_string_choice_localized( "Welcome to the club, you're now a good person. Well, I hope.", "club", - [( + new_map( "de", "Willkommen im Club, du bist jetzt ein guter Mensch. Naja, hoffentlich.", - )], + ), ) .add_string_choice_localized( "I hope that you brought a controller to play together!", "game", - [("de", "Ich hoffe du hast einen Controller zum Spielen mitgebracht!")], + new_map("de", "Ich hoffe du hast einen Controller zum Spielen mitgebracht!"), ), ) } diff --git a/examples/e14_slash_commands/src/commands/wonderful_command.rs b/examples/e14_slash_commands/src/commands/wonderful_command.rs index 95e4f1761d8..d1f991a6427 100644 --- a/examples/e14_slash_commands/src/commands/wonderful_command.rs +++ b/examples/e14_slash_commands/src/commands/wonderful_command.rs @@ -1,5 +1,5 @@ use serenity::builder::CreateCommand; -pub fn register() -> CreateCommand { +pub fn register() -> CreateCommand<'static> { CreateCommand::new("wonderful_command").description("An amazing command") } diff --git a/examples/e14_slash_commands/src/main.rs b/examples/e14_slash_commands/src/main.rs index 70e5cea2a29..ac2ab28f6e3 100644 --- a/examples/e14_slash_commands/src/main.rs +++ b/examples/e14_slash_commands/src/main.rs @@ -49,7 +49,7 @@ impl EventHandler for Handler { ); let commands = guild_id - .set_commands(&ctx.http, vec![ + .set_commands(&ctx.http, &[ commands::ping::register(), commands::id::register(), commands::welcome::register(), diff --git a/examples/e17_message_components/src/main.rs b/examples/e17_message_components/src/main.rs index c70203354a9..81d3b944d78 100644 --- a/examples/e17_message_components/src/main.rs +++ b/examples/e17_message_components/src/main.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::env; use std::time::Duration; @@ -39,13 +40,13 @@ impl EventHandler for Handler { &ctx, CreateMessage::new().content("Please select your favorite animal").select_menu( CreateSelectMenu::new("animal_select", CreateSelectMenuKind::String { - options: vec![ + options: Cow::Borrowed(&[ CreateSelectMenuOption::new("🐈 meow", "Cat"), CreateSelectMenuOption::new("🐕 woof", "Dog"), CreateSelectMenuOption::new("🐎 neigh", "Horse"), CreateSelectMenuOption::new("🦙 hoooooooonk", "Alpaca"), CreateSelectMenuOption::new("🦀 crab rave", "Ferris"), - ], + ]), }) .custom_id("animal_select") .placeholder("No animal selected"), diff --git a/examples/e19_interactions_endpoint/src/main.rs b/examples/e19_interactions_endpoint/src/main.rs index 3f4c784ea63..a3ae19c5943 100644 --- a/examples/e19_interactions_endpoint/src/main.rs +++ b/examples/e19_interactions_endpoint/src/main.rs @@ -5,7 +5,7 @@ use serenity::model::application::*; type Error = Box; -fn handle_command(interaction: CommandInteraction) -> CreateInteractionResponse { +fn handle_command(interaction: CommandInteraction) -> CreateInteractionResponse<'static> { CreateInteractionResponse::Message(CreateInteractionResponseMessage::new().content(format!( "Hello from interactions webhook HTTP server! <@{}>", interaction.user.id diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 46032b7a93c..cb4889c5416 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use serenity::builder::*; use serenity::model::prelude::*; use serenity::prelude::*; @@ -98,10 +100,10 @@ async fn message(ctx: &Context, msg: Message) -> Result<(), serenity::Error> { CreateButton::new_link("https://google.com").emoji('🔍').label("Search"), ) .select_menu(CreateSelectMenu::new("3", CreateSelectMenuKind::String { - options: vec![ + options: Cow::Borrowed(&[ CreateSelectMenuOption::new("foo", "foo"), CreateSelectMenuOption::new("bar", "bar"), - ], + ]), })), ) .await?; @@ -162,7 +164,8 @@ async fn message(ctx: &Context, msg: Message) -> Result<(), serenity::Error> { channel_id .edit_thread( &ctx, - EditThread::new().applied_tags(forum.available_tags.iter().map(|t| t.id)), + EditThread::new() + .applied_tags(forum.available_tags.iter().map(|t| t.id).collect::>()), ) .await?; } else if msg.content == "embedrace" { @@ -342,10 +345,10 @@ async fn interaction( CreateInteractionResponse::Message( CreateInteractionResponseMessage::new() .select_menu(CreateSelectMenu::new("0", CreateSelectMenuKind::String { - options: vec![ + options: Cow::Borrowed(&[ CreateSelectMenuOption::new("foo", "foo"), CreateSelectMenuOption::new("bar", "bar"), - ], + ]), })) .select_menu(CreateSelectMenu::new( "1", diff --git a/src/builder/add_member.rs b/src/builder/add_member.rs index 2e1dd13d768..beab54c84e5 100644 --- a/src/builder/add_member.rs +++ b/src/builder/add_member.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -11,25 +13,25 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/guild#add-guild-member). #[derive(Clone, Debug, Serialize)] #[must_use] -pub struct AddMember { - access_token: String, +pub struct AddMember<'a> { + access_token: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] - nick: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - roles: Vec, + nick: Option>, + #[serde(skip_serializing_if = "<[RoleId]>::is_empty")] + roles: Cow<'a, [RoleId]>, #[serde(skip_serializing_if = "Option::is_none")] mute: Option, #[serde(skip_serializing_if = "Option::is_none")] deaf: Option, } -impl AddMember { +impl<'a> AddMember<'a> { /// Constructs a new builder with the given access token, leaving all other fields empty. - pub fn new(access_token: String) -> Self { + pub fn new(access_token: impl Into>) -> Self { Self { - access_token, + access_token: access_token.into(), + roles: Cow::default(), nick: None, - roles: Vec::new(), mute: None, deaf: None, } @@ -38,7 +40,7 @@ impl AddMember { /// Sets the OAuth2 access token for this request, replacing the current one. /// /// Requires the access token to have the `guilds.join` scope granted. - pub fn access_token(mut self, access_token: impl Into) -> Self { + pub fn access_token(mut self, access_token: impl Into>) -> Self { self.access_token = access_token.into(); self } @@ -48,7 +50,7 @@ impl AddMember { /// Requires the [Manage Nicknames] permission. /// /// [Manage Nicknames]: crate::model::permissions::Permissions::MANAGE_NICKNAMES - pub fn nickname(mut self, nickname: impl Into) -> Self { + pub fn nickname(mut self, nickname: impl Into>) -> Self { self.nick = Some(nickname.into()); self } @@ -58,8 +60,8 @@ impl AddMember { /// Requires the [Manage Roles] permission. /// /// [Manage Roles]: crate::model::permissions::Permissions::MANAGE_ROLES - pub fn roles(mut self, roles: impl IntoIterator>) -> Self { - self.roles = roles.into_iter().map(Into::into).collect(); + pub fn roles(mut self, roles: impl Into>) -> Self { + self.roles = roles.into(); self } @@ -86,7 +88,7 @@ impl AddMember { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for AddMember { +impl Builder for AddMember<'_> { type Context<'ctx> = (GuildId, UserId); type Built = Option; diff --git a/src/builder/bot_auth_parameters.rs b/src/builder/bot_auth_parameters.rs index 8fa4c5f4321..acdd94f7cf6 100644 --- a/src/builder/bot_auth_parameters.rs +++ b/src/builder/bot_auth_parameters.rs @@ -1,3 +1,6 @@ +use std::borrow::Cow; +use std::fmt::Write; + use arrayvec::ArrayVec; use url::Url; @@ -7,18 +10,28 @@ use crate::http::Http; use crate::internal::prelude::*; use crate::model::prelude::*; +fn join_to_string(sep: char, iter: impl Iterator) -> String { + let mut buf = String::new(); + for item in iter { + write!(buf, "{item}{sep}").unwrap(); + } + + buf.truncate(buf.len() - 1); + buf +} + /// A builder for constructing an invite link with custom OAuth2 scopes. #[derive(Debug, Clone, Default)] #[must_use] -pub struct CreateBotAuthParameters { +pub struct CreateBotAuthParameters<'a> { client_id: Option, - scopes: Vec, + scopes: Cow<'a, [Scope]>, permissions: Permissions, guild_id: Option, disable_guild_select: bool, } -impl CreateBotAuthParameters { +impl<'a> CreateBotAuthParameters<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -35,10 +48,7 @@ impl CreateBotAuthParameters { } if !self.scopes.is_empty() { - valid_data.push(( - "scope", - self.scopes.iter().map(ToString::to_string).collect::>().join(" "), - )); + valid_data.push(("scope", join_to_string(',', self.scopes.iter()))); } if bits != 0 { @@ -84,8 +94,8 @@ impl CreateBotAuthParameters { /// **Note**: This needs to include the [`Bot`] scope. /// /// [`Bot`]: Scope::Bot - pub fn scopes(mut self, scopes: &[Scope]) -> Self { - self.scopes = scopes.to_vec(); + pub fn scopes(mut self, scopes: impl Into>) -> Self { + self.scopes = scopes.into(); self } diff --git a/src/builder/create_allowed_mentions.rs b/src/builder/create_allowed_mentions.rs index 6655c967405..f739fcfcf05 100644 --- a/src/builder/create_allowed_mentions.rs +++ b/src/builder/create_allowed_mentions.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use arrayvec::ArrayVec; use serde::{Deserialize, Serialize}; @@ -34,17 +36,20 @@ impl ParseAction { /// ```rust,no_run /// # use serenity::builder::CreateMessage; /// # use serenity::model::channel::Message; +/// # use serenity::model::id::*; /// # /// # async fn run() -> Result<(), Box> { /// use serenity::builder::CreateAllowedMentions as Am; /// /// // Mention only the user 110372470472613888 /// # let m = CreateMessage::new(); -/// m.allowed_mentions(Am::new().users(vec![110372470472613888])); +/// m.allowed_mentions(Am::new().users([UserId::new(110372470472613888)].as_slice())); /// /// // Mention all users and the role 182894738100322304 /// # let m = CreateMessage::new(); -/// m.allowed_mentions(Am::new().all_users(true).roles(vec![182894738100322304])); +/// m.allowed_mentions( +/// Am::new().all_users(true).roles([RoleId::new(182894738100322304)].as_slice()), +/// ); /// /// // Mention all roles and nothing else /// # let m = CreateMessage::new(); @@ -57,13 +62,15 @@ impl ParseAction { /// // Mention everyone and the users 182891574139682816, 110372470472613888 /// # let m = CreateMessage::new(); /// m.allowed_mentions( -/// Am::new().everyone(true).users(vec![182891574139682816, 110372470472613888]), +/// Am::new() +/// .everyone(true) +/// .users([UserId::new(182891574139682816), UserId::new(110372470472613888)].as_slice()), /// ); /// /// // Mention everyone and the message author. /// # let m = CreateMessage::new(); /// # let msg: Message = unimplemented!(); -/// m.allowed_mentions(Am::new().everyone(true).users(vec![msg.author.id])); +/// m.allowed_mentions(Am::new().everyone(true).users([msg.author.id].as_slice())); /// # Ok(()) /// # } /// ``` @@ -71,15 +78,15 @@ impl ParseAction { /// [Discord docs](https://discord.com/developers/docs/resources/channel#allowed-mentions-object). #[derive(Clone, Debug, Default, Serialize, PartialEq)] #[must_use] -pub struct CreateAllowedMentions { +pub struct CreateAllowedMentions<'a> { parse: ArrayVec, - users: Vec, - roles: Vec, + users: Cow<'a, [UserId]>, + roles: Cow<'a, [RoleId]>, #[serde(skip_serializing_if = "Option::is_none")] replied_user: Option, } -impl CreateAllowedMentions { +impl<'a> CreateAllowedMentions<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -113,29 +120,29 @@ impl CreateAllowedMentions { /// Sets the *specific* users that will be allowed mentionable. #[inline] - pub fn users(mut self, users: impl IntoIterator>) -> Self { - self.users = users.into_iter().map(Into::into).collect(); + pub fn users(mut self, users: impl Into>) -> Self { + self.users = users.into(); self } /// Clear the list of mentionable users. #[inline] pub fn empty_users(mut self) -> Self { - self.users.clear(); + self.users = Cow::default(); self } /// Sets the *specific* roles that will be allowed mentionable. #[inline] - pub fn roles(mut self, roles: impl IntoIterator>) -> Self { - self.roles = roles.into_iter().map(Into::into).collect(); + pub fn roles(mut self, roles: impl Into>) -> Self { + self.roles = roles.into(); self } /// Clear the list of mentionable roles. #[inline] pub fn empty_roles(mut self) -> Self { - self.roles.clear(); + self.roles = Cow::default(); self } diff --git a/src/builder/create_attachment.rs b/src/builder/create_attachment.rs index ddbca56bc1f..ed9e9d9c9fe 100644 --- a/src/builder/create_attachment.rs +++ b/src/builder/create_attachment.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::path::Path; use tokio::fs::File; @@ -21,18 +22,21 @@ use crate::model::id::AttachmentId; #[derive(Clone, Debug, Serialize, PartialEq)] #[non_exhaustive] #[must_use] -pub struct CreateAttachment { +pub struct CreateAttachment<'a> { pub(crate) id: u64, // Placeholder ID will be filled in when sending the request - pub filename: String, - pub description: Option, + pub filename: Cow<'static, str>, + pub description: Option>, #[serde(skip)] - pub data: Vec, + pub data: Cow<'static, [u8]>, } -impl CreateAttachment { +impl<'a> CreateAttachment<'a> { /// Builds an [`CreateAttachment`] from the raw attachment data. - pub fn bytes(data: impl Into>, filename: impl Into) -> CreateAttachment { + pub fn bytes( + data: impl Into>, + filename: impl Into>, + ) -> Self { CreateAttachment { data: data.into(), filename: filename.into(), @@ -46,7 +50,7 @@ impl CreateAttachment { /// # Errors /// /// [`Error::Io`] if reading the file fails. - pub async fn path(path: impl AsRef) -> Result { + pub async fn path(path: impl AsRef) -> Result { let mut file = File::open(path.as_ref()).await?; let mut data = Vec::new(); file.read_to_end(&mut data).await?; @@ -58,7 +62,7 @@ impl CreateAttachment { ) })?; - Ok(CreateAttachment::bytes(data, filename.to_string_lossy().to_string())) + Ok(CreateAttachment::bytes(data, filename.to_string_lossy().into_owned())) } /// Builds an [`CreateAttachment`] by reading from a file handler. @@ -66,7 +70,7 @@ impl CreateAttachment { /// # Errors /// /// [`Error::Io`] error if reading the file fails. - pub async fn file(file: &File, filename: impl Into) -> Result { + pub async fn file(file: &File, filename: impl Into>) -> Result { let mut data = Vec::new(); file.try_clone().await?.read_to_end(&mut data).await?; @@ -79,7 +83,7 @@ impl CreateAttachment { /// /// [`Error::Url`] if the URL is invalid, [`Error::Http`] if downloading the data fails. #[cfg(feature = "http")] - pub async fn url(http: impl AsRef, url: &str) -> Result { + pub async fn url(http: impl AsRef, url: &str) -> Result { let url = Url::parse(url).map_err(|_| Error::Url(url.to_string()))?; let response = http.as_ref().client.get(url.clone()).send().await?; @@ -90,7 +94,7 @@ impl CreateAttachment { .and_then(Iterator::last) .ok_or_else(|| Error::Url(url.to_string()))?; - Ok(CreateAttachment::bytes(data, filename)) + Ok(CreateAttachment::bytes(data, filename.to_owned())) } /// Converts the stored data to the base64 representation. @@ -108,7 +112,7 @@ impl CreateAttachment { } /// Sets a description for the file (max 1024 characters). - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -121,8 +125,8 @@ struct ExistingAttachment { #[derive(Debug, Clone, serde::Serialize, PartialEq)] #[serde(untagged)] -enum NewOrExisting { - New(CreateAttachment), +enum NewOrExisting<'a> { + New(CreateAttachment<'a>), Existing(ExistingAttachment), } @@ -148,7 +152,7 @@ enum NewOrExisting { /// /// ```rust,no_run /// # use serenity::all::*; -/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment) -> Result<(), Error> { +/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment<'_>) -> Result<(), Error> { /// msg.edit(ctx, EditMessage::new().attachments( /// EditAttachments::keep_all(&msg).add(my_attachment) /// )).await?; @@ -159,7 +163,7 @@ enum NewOrExisting { /// /// ```rust,no_run /// # use serenity::all::*; -/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment) -> Result<(), Error> { +/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment<'_>) -> Result<(), Error> { /// msg.edit(ctx, EditMessage::new().attachments( /// EditAttachments::new().keep(msg.attachments[0].id) /// )).await?; @@ -170,7 +174,7 @@ enum NewOrExisting { /// /// ```rust,no_run /// # use serenity::all::*; -/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment) -> Result<(), Error> { +/// # async fn _foo(ctx: Http, mut msg: Message, my_attachment: CreateAttachment<'_>) -> Result<(), Error> { /// msg.edit(ctx, EditMessage::new().attachments( /// EditAttachments::keep_all(&msg).remove(msg.attachments[0].id) /// )).await?; @@ -184,11 +188,11 @@ enum NewOrExisting { #[derive(Default, Debug, Clone, serde::Serialize, PartialEq)] #[serde(transparent)] #[must_use] -pub struct EditAttachments { - new_and_existing_attachments: Vec, +pub struct EditAttachments<'a> { + new_and_existing_attachments: Vec>, } -impl EditAttachments { +impl<'a> EditAttachments<'a> { /// An empty attachments builder. /// /// Existing attachments are not kept by default, either. See [`Self::keep_all()`] or @@ -247,7 +251,7 @@ impl EditAttachments { /// Adds a new attachment to the attachment list. #[allow(clippy::should_implement_trait)] // Clippy thinks add == std::ops::Add::add - pub fn add(mut self, attachment: CreateAttachment) -> Self { + pub fn add(mut self, attachment: CreateAttachment<'a>) -> Self { self.new_and_existing_attachments.push(NewOrExisting::New(attachment)); self } @@ -255,7 +259,7 @@ impl EditAttachments { /// Clones all new attachments into a new Vec, keeping only data and filename, because those /// are needed for the multipart form data. The data is taken out of `self` in the process, so /// this method can only be called once. - pub(crate) fn take_files(&mut self) -> Vec { + pub(crate) fn take_files(&mut self) -> Vec> { let mut id_placeholder = 0; let mut files = Vec::new(); diff --git a/src/builder/create_channel.rs b/src/builder/create_channel.rs index 981b5de1f07..34cc6424fd5 100644 --- a/src/builder/create_channel.rs +++ b/src/builder/create_channel.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -14,12 +16,12 @@ use crate::model::prelude::*; #[derive(Clone, Debug, Serialize)] #[must_use] pub struct CreateChannel<'a> { - name: String, + name: Cow<'a, str>, #[serde(rename = "type")] kind: ChannelType, #[serde(skip_serializing_if = "Option::is_none")] - topic: Option, + topic: Option>, #[serde(skip_serializing_if = "Option::is_none")] bitrate: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -28,22 +30,22 @@ pub struct CreateChannel<'a> { rate_limit_per_user: Option, #[serde(skip_serializing_if = "Option::is_none")] position: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - permission_overwrites: Vec, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + permission_overwrites: Cow<'a, [PermissionOverwrite]>, #[serde(skip_serializing_if = "Option::is_none")] parent_id: Option, #[serde(skip_serializing_if = "Option::is_none")] nsfw: Option, #[serde(skip_serializing_if = "Option::is_none")] - rtc_region: Option, + rtc_region: Option>, #[serde(skip_serializing_if = "Option::is_none")] video_quality_mode: Option, #[serde(skip_serializing_if = "Option::is_none")] default_auto_archive_duration: Option, #[serde(skip_serializing_if = "Option::is_none")] default_reaction_emoji: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - available_tags: Vec, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + available_tags: Cow<'a, [ForumTag]>, #[serde(skip_serializing_if = "Option::is_none")] default_sort_order: Option, @@ -54,7 +56,7 @@ pub struct CreateChannel<'a> { impl<'a> CreateChannel<'a> { /// Creates a builder with the given name, setting [`Self::kind`] to [`ChannelType::Text`] and /// leaving all other fields empty. - pub fn new(name: impl Into) -> Self { + pub fn new(name: impl Into>) -> Self { Self { name: name.into(), nsfw: None, @@ -65,13 +67,13 @@ impl<'a> CreateChannel<'a> { user_limit: None, rate_limit_per_user: None, kind: ChannelType::Text, - permission_overwrites: Vec::new(), + permission_overwrites: Cow::default(), audit_log_reason: None, rtc_region: None, video_quality_mode: None, default_auto_archive_duration: None, default_reaction_emoji: None, - available_tags: Vec::new(), + available_tags: Cow::default(), default_sort_order: None, } } @@ -79,7 +81,7 @@ impl<'a> CreateChannel<'a> { /// Specify how to call this new channel, replacing the current value as set in [`Self::new`]. /// /// **Note**: Must be between 2 and 100 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } @@ -103,7 +105,7 @@ impl<'a> CreateChannel<'a> { /// Channel topic (0-1024 characters) /// /// Only for [`ChannelType::Text`], [`ChannelType::News`], [`ChannelType::Forum`] - pub fn topic(mut self, topic: impl Into) -> Self { + pub fn topic(mut self, topic: impl Into>) -> Self { self.topic = Some(topic.into()); self } @@ -190,8 +192,8 @@ impl<'a> CreateChannel<'a> { /// # Ok(()) /// # } /// ``` - pub fn permissions(mut self, perms: impl IntoIterator) -> Self { - self.permission_overwrites = perms.into_iter().map(Into::into).collect(); + pub fn permissions(mut self, perms: impl Into>) -> Self { + self.permission_overwrites = perms.into(); self } @@ -204,7 +206,7 @@ impl<'a> CreateChannel<'a> { /// Channel voice region id of the voice or stage channel, automatic when not set /// /// Only for [`ChannelType::Voice`] and [`ChannelType::Stage`] - pub fn rtc_region(mut self, rtc_region: String) -> Self { + pub fn rtc_region(mut self, rtc_region: Cow<'a, str>) -> Self { self.rtc_region = Some(rtc_region); self } @@ -240,8 +242,8 @@ impl<'a> CreateChannel<'a> { /// Set of tags that can be used in a forum channel /// /// Only for [`ChannelType::Forum`] - pub fn available_tags(mut self, available_tags: impl IntoIterator) -> Self { - self.available_tags = available_tags.into_iter().collect(); + pub fn available_tags(mut self, available_tags: impl Into>) -> Self { + self.available_tags = available_tags.into(); self } diff --git a/src/builder/create_command.rs b/src/builder/create_command.rs index 5e6d385ae75..4d19003429c 100644 --- a/src/builder/create_command.rs +++ b/src/builder/create_command.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -14,45 +16,73 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). #[derive(Clone, Debug, Serialize)] #[must_use] -pub struct CreateCommandOption(CommandOption); +pub struct CreateCommandOption<'a> { + #[serde(rename = "type")] + kind: CommandOptionType, + name: Cow<'a, str>, + #[serde(skip_serializing_if = "Option::is_none")] + name_localizations: Option, Cow<'a, str>>>, + description: Cow<'a, str>, + #[serde(skip_serializing_if = "Option::is_none")] + description_localizations: Option, Cow<'a, str>>>, + #[serde(default)] + required: bool, + #[serde(default)] + choices: Cow<'a, [CreateCommandOptionChoice<'a>]>, + #[serde(default)] + options: Cow<'a, [CreateCommandOption<'a>]>, + #[serde(default)] + channel_types: Cow<'a, [ChannelType]>, + #[serde(default)] + min_value: Option, + #[serde(default)] + max_value: Option, + #[serde(default)] + min_length: Option, + #[serde(default)] + max_length: Option, + #[serde(default)] + autocomplete: bool, +} -impl CreateCommandOption { +impl<'a> CreateCommandOption<'a> { /// Creates a new builder with the given option type, name, and description, leaving all other /// fields empty. pub fn new( kind: CommandOptionType, - name: impl Into, - description: impl Into, + name: impl Into>, + description: impl Into>, ) -> Self { - Self(CommandOption { + Self { kind, - name: name.into().into(), + name: name.into(), name_localizations: None, - description: description.into().into(), + description: description.into(), description_localizations: None, - __generated_flags: CommandOptionGeneratedFlags::empty(), + required: false, + autocomplete: false, min_value: None, max_value: None, min_length: None, max_length: None, - channel_types: FixedArray::default(), - choices: Vec::new(), - options: Vec::new(), - }) + channel_types: Cow::default(), + choices: Cow::default(), + options: Cow::default(), + } } /// Sets the `CommandOptionType`, replacing the current value as set in [`Self::new`]. pub fn kind(mut self, kind: CommandOptionType) -> Self { - self.0.kind = kind; + self.kind = kind; self } /// Sets the name of the option, replacing the current value as set in [`Self::new`]. /// /// **Note**: Must be between 1 and 32 lowercase characters, matching `r"^[\w-]{1,32}$"`. - pub fn name(mut self, name: impl Into) -> Self { - self.0.name = name.into().into(); + pub fn name(mut self, name: impl Into>) -> Self { + self.name = name.into(); self } @@ -66,8 +96,12 @@ impl CreateCommandOption { /// .name_localized("zh-CN", "岁数") /// # ; /// ``` - pub fn name_localized(mut self, locale: impl Into, name: impl Into) -> Self { - let map = self.0.name_localizations.get_or_insert_with(Default::default); + pub fn name_localized( + mut self, + locale: impl Into>, + name: impl Into>, + ) -> Self { + let map = self.name_localizations.get_or_insert_with(Default::default); map.insert(locale.into(), name.into()); self } @@ -75,8 +109,8 @@ impl CreateCommandOption { /// Sets the description for the option, replacing the current value as set in [`Self::new`]. /// /// **Note**: Must be between 1 and 100 characters. - pub fn description(mut self, description: impl Into) -> Self { - self.0.description = description.into().into(); + pub fn description(mut self, description: impl Into>) -> Self { + self.description = description.into(); self } /// Specifies a localized description of the option. @@ -91,10 +125,10 @@ impl CreateCommandOption { /// ``` pub fn description_localized( mut self, - locale: impl Into, - description: impl Into, + locale: impl Into>, + description: impl Into>, ) -> Self { - let map = self.0.description_localizations.get_or_insert_with(Default::default); + let map = self.description_localizations.get_or_insert_with(Default::default); map.insert(locale.into(), description.into()); self } @@ -103,7 +137,7 @@ impl CreateCommandOption { /// /// **Note**: This defaults to `false`. pub fn required(mut self, required: bool) -> Self { - self.0.set_required(required); + self.required = required; self } @@ -111,9 +145,9 @@ impl CreateCommandOption { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be between -2^53 and 2^53. - pub fn add_int_choice(self, name: impl Into, value: i64) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), + pub fn add_int_choice(self, name: impl Into>, value: i64) -> Self { + self.add_choice(CreateCommandOptionChoice { + name: name.into(), value: Value::from(value), name_localizations: None, }) @@ -122,16 +156,14 @@ impl CreateCommandOption { /// Adds a localized optional int-choice. See [`Self::add_int_choice`] for more info. pub fn add_int_choice_localized( self, - name: impl Into, + name: impl Into>, value: i64, - locales: impl IntoIterator, impl Into)>, + locales: impl Into, Cow<'a, str>>>, ) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), - value: Value::from(value), - name_localizations: Some( - locales.into_iter().map(|(l, n)| (l.into(), n.into())).collect(), - ), + self.add_choice(CreateCommandOptionChoice { + name: name.into(), + value: value.into(), + name_localizations: Some(locales.into()), }) } @@ -139,9 +171,13 @@ impl CreateCommandOption { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be up to 100 characters. - pub fn add_string_choice(self, name: impl Into, value: impl Into) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), + pub fn add_string_choice( + self, + name: impl Into>, + value: impl Into, + ) -> Self { + self.add_choice(CreateCommandOptionChoice { + name: name.into(), value: Value::String(value.into()), name_localizations: None, }) @@ -150,16 +186,14 @@ impl CreateCommandOption { /// Adds a localized optional string-choice. See [`Self::add_string_choice`] for more info. pub fn add_string_choice_localized( self, - name: impl Into, + name: impl Into>, value: impl Into, - locales: impl IntoIterator, impl Into)>, + locales: impl Into, Cow<'a, str>>>, ) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), + self.add_choice(CreateCommandOptionChoice { + name: name.into(), value: Value::String(value.into()), - name_localizations: Some( - locales.into_iter().map(|(l, n)| (l.into(), n.into())).collect(), - ), + name_localizations: Some(locales.into()), }) } @@ -167,9 +201,9 @@ impl CreateCommandOption { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be between -2^53 and 2^53. - pub fn add_number_choice(self, name: impl Into, value: f64) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), + pub fn add_number_choice(self, name: impl Into>, value: f64) -> Self { + self.add_choice(CreateCommandOptionChoice { + name: name.into(), value: Value::from(value), name_localizations: None, }) @@ -178,21 +212,19 @@ impl CreateCommandOption { /// Adds a localized optional number-choice. See [`Self::add_number_choice`] for more info. pub fn add_number_choice_localized( self, - name: impl Into, + name: impl Into>, value: f64, - locales: impl IntoIterator, impl Into)>, + locales: impl Into, Cow<'a, str>>>, ) -> Self { - self.add_choice(CommandOptionChoice { - name: name.into().into(), + self.add_choice(CreateCommandOptionChoice { + name: name.into(), value: Value::from(value), - name_localizations: Some( - locales.into_iter().map(|(l, n)| (l.into(), n.into())).collect(), - ), + name_localizations: Some(locales.into()), }) } - fn add_choice(mut self, value: CommandOptionChoice) -> Self { - self.0.choices.push(value); + fn add_choice(mut self, value: CreateCommandOptionChoice<'a>) -> Self { + self.choices.to_mut().push(value); self } @@ -202,7 +234,7 @@ impl CreateCommandOption { /// - May not be set to `true` if `choices` are set /// - Options using `autocomplete` are not confined to only use given choices pub fn set_autocomplete(mut self, value: bool) -> Self { - self.0.set_autocomplete(value); + self.autocomplete = value; self } @@ -218,9 +250,9 @@ impl CreateCommandOption { /// [`SubCommand`]: crate::model::application::CommandOptionType::SubCommand pub fn set_sub_options( mut self, - sub_options: impl IntoIterator, + sub_options: impl Into]>>, ) -> Self { - self.0.options = sub_options.into_iter().map(|o| o.0).collect(); + self.options = sub_options.into(); self } @@ -231,40 +263,40 @@ impl CreateCommandOption { /// /// [`SubCommandGroup`]: crate::model::application::CommandOptionType::SubCommandGroup /// [`SubCommand`]: crate::model::application::CommandOptionType::SubCommand - pub fn add_sub_option(mut self, sub_option: CreateCommandOption) -> Self { - self.0.options.push(sub_option.0); + pub fn add_sub_option(mut self, sub_option: CreateCommandOption<'a>) -> Self { + self.options.to_mut().push(sub_option); self } /// If the option is a [`Channel`], it will only be able to show these types. /// /// [`Channel`]: crate::model::application::CommandOptionType::Channel - pub fn channel_types(mut self, channel_types: Vec) -> Self { - self.0.channel_types = channel_types.into(); + pub fn channel_types(mut self, channel_types: impl Into>) -> Self { + self.channel_types = channel_types.into(); self } /// Sets the minimum permitted value for this integer option pub fn min_int_value(mut self, value: i64) -> Self { - self.0.min_value = Some(value.into()); + self.min_value = Some(value.into()); self } /// Sets the maximum permitted value for this integer option pub fn max_int_value(mut self, value: i64) -> Self { - self.0.max_value = Some(value.into()); + self.max_value = Some(value.into()); self } /// Sets the minimum permitted value for this number option pub fn min_number_value(mut self, value: f64) -> Self { - self.0.min_value = serde_json::Number::from_f64(value); + self.min_value = serde_json::Number::from_f64(value); self } /// Sets the maximum permitted value for this number option pub fn max_number_value(mut self, value: f64) -> Self { - self.0.max_value = serde_json::Number::from_f64(value); + self.max_value = serde_json::Number::from_f64(value); self } @@ -272,7 +304,7 @@ impl CreateCommandOption { /// /// The value of `min_length` must be greater or equal to `0`. pub fn min_length(mut self, value: u16) -> Self { - self.0.min_length = Some(value); + self.min_length = Some(value); self } @@ -281,7 +313,7 @@ impl CreateCommandOption { /// /// The value of `max_length` must be greater or equal to `1`. pub fn max_length(mut self, value: u16) -> Self { - self.0.max_length = Some(value); + self.max_length = Some(value); self } @@ -298,15 +330,15 @@ impl CreateCommandOption { /// - [guild command](https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command-json-params) #[derive(Clone, Debug, Serialize)] #[must_use] -pub struct CreateCommand { - name: FixedString, - name_localizations: HashMap, +pub struct CreateCommand<'a> { + name: Cow<'a, str>, + name_localizations: HashMap, Cow<'a, str>>, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, - description_localizations: HashMap, - options: Vec, + description: Option>, + description_localizations: HashMap, Cow<'a, str>>, + options: Cow<'a, [CreateCommandOption<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] - default_member_permissions: Option, + default_member_permissions: Option, #[serde(skip_serializing_if = "Option::is_none")] dm_permission: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -321,13 +353,13 @@ pub struct CreateCommand { nsfw: bool, } -impl CreateCommand { +impl<'a> CreateCommand<'a> { /// Creates a new builder with the given name, leaving all other fields empty. - pub fn new(name: impl Into) -> Self { + pub fn new(name: impl Into>) -> Self { Self { kind: None, - name: name.into().into(), + name: name.into(), name_localizations: HashMap::new(), description: None, description_localizations: HashMap::new(), @@ -339,7 +371,7 @@ impl CreateCommand { #[cfg(feature = "unstable_discord_api")] contexts: None, - options: Vec::new(), + options: Cow::default(), nsfw: false, } } @@ -350,8 +382,8 @@ impl CreateCommand { /// **Note**: Must be between 1 and 32 lowercase characters, matching `r"^[\w-]{1,32}$"`. Two /// global commands of the same app cannot have the same name. Two guild-specific commands of /// the same app cannot have the same name. - pub fn name(mut self, name: impl Into) -> Self { - self.name = name.into().into(); + pub fn name(mut self, name: impl Into>) -> Self { + self.name = name.into(); self } @@ -364,7 +396,11 @@ impl CreateCommand { /// .name_localized("el", "γενέθλια") /// # ; /// ``` - pub fn name_localized(mut self, locale: impl Into, name: impl Into) -> Self { + pub fn name_localized( + mut self, + locale: impl Into>, + name: impl Into>, + ) -> Self { self.name_localizations.insert(locale.into(), name.into()); self } @@ -377,7 +413,7 @@ impl CreateCommand { /// Specifies the default permissions required to execute the command. pub fn default_member_permissions(mut self, permissions: Permissions) -> Self { - self.default_member_permissions = Some(permissions.bits().to_string()); + self.default_member_permissions = Some(permissions); self } @@ -391,7 +427,7 @@ impl CreateCommand { /// Specifies the description of the application command. /// /// **Note**: Must be between 1 and 100 characters long. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -406,8 +442,8 @@ impl CreateCommand { /// ``` pub fn description_localized( mut self, - locale: impl Into, - description: impl Into, + locale: impl Into>, + description: impl Into>, ) -> Self { self.description_localizations.insert(locale.into(), description.into()); self @@ -416,16 +452,16 @@ impl CreateCommand { /// Adds an application command option for the application command. /// /// **Note**: Application commands can have up to 25 options. - pub fn add_option(mut self, option: CreateCommandOption) -> Self { - self.options.push(option); + pub fn add_option(mut self, option: CreateCommandOption<'a>) -> Self { + self.options.to_mut().push(option); self } /// Sets all the application command options for the application command. /// /// **Note**: Application commands can have up to 25 options. - pub fn set_options(mut self, options: Vec) -> Self { - self.options = options; + pub fn set_options(mut self, options: impl Into]>>) -> Self { + self.options = options.into(); self } @@ -466,7 +502,7 @@ impl CreateCommand { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for CreateCommand { +impl Builder for CreateCommand<'_> { type Context<'ctx> = (Option, Option); type Built = Command; @@ -500,3 +536,11 @@ impl Builder for CreateCommand { } } } + +#[derive(Clone, Debug, Serialize)] +struct CreateCommandOptionChoice<'a> { + pub name: Cow<'a, str>, + #[serde(skip_serializing_if = "Option::is_none")] + pub name_localizations: Option, Cow<'a, str>>>, + pub value: Value, +} diff --git a/src/builder/create_command_permission.rs b/src/builder/create_command_permission.rs index 9177ea4651f..fad0fb5ada8 100644 --- a/src/builder/create_command_permission.rs +++ b/src/builder/create_command_permission.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -14,21 +16,21 @@ use crate::model::prelude::*; // `permissions` is added to the HTTP endpoint #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct EditCommandPermissions { - permissions: Vec, +pub struct EditCommandPermissions<'a> { + permissions: Cow<'a, [CreateCommandPermission]>, } -impl EditCommandPermissions { - pub fn new(permissions: Vec) -> Self { +impl<'a> EditCommandPermissions<'a> { + pub fn new(permissions: impl Into>) -> Self { Self { - permissions, + permissions: permissions.into(), } } } #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for EditCommandPermissions { +impl Builder for EditCommandPermissions<'_> { type Context<'ctx> = (GuildId, CommandId); type Built = CommandPermissions; diff --git a/src/builder/create_components.rs b/src/builder/create_components.rs index 35f3f505096..461fa7dd9ef 100644 --- a/src/builder/create_components.rs +++ b/src/builder/create_components.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use serde::Serialize; use crate::json::{self, json}; @@ -8,14 +10,14 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/interactions/message-components#component-object). #[derive(Clone, Debug, PartialEq)] #[must_use] -pub enum CreateActionRow { - Buttons(Vec), - SelectMenu(CreateSelectMenu), +pub enum CreateActionRow<'a> { + Buttons(Vec>), + SelectMenu(CreateSelectMenu<'a>), /// Only valid in modals! - InputText(CreateInputText), + InputText(CreateInputText<'a>), } -impl serde::Serialize for CreateActionRow { +impl<'a> serde::Serialize for CreateActionRow<'a> { fn serialize(&self, serializer: S) -> Result { use serde::ser::Error as _; @@ -34,51 +36,62 @@ impl serde::Serialize for CreateActionRow { /// A builder for creating a button component in a message #[derive(Clone, Debug, Serialize, PartialEq)] #[must_use] -pub struct CreateButton(Button); +pub struct CreateButton<'a> { + style: ButtonStyle, + #[serde(rename = "type")] + kind: ComponentType, + #[serde(skip_serializing_if = "Option::is_none")] + url: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + label: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + custom_id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + emoji: Option, + #[serde(default)] + disabled: bool, +} -impl CreateButton { +impl<'a> CreateButton<'a> { /// Creates a link button to the given URL. You must also set [`Self::label`] and/or /// [`Self::emoji`] after this. /// /// Clicking this button _will not_ trigger an interaction event in your bot. - pub fn new_link(url: impl Into) -> Self { - Self(Button { + pub fn new_link(url: impl Into>) -> Self { + Self { + style: ButtonStyle::Unknown(5), kind: ComponentType::Button, - data: ButtonKind::Link { - url: url.into().into(), - }, + url: Some(url.into()), + custom_id: None, label: None, emoji: None, disabled: false, - }) + } } /// Creates a normal button with the given custom ID. You must also set [`Self::label`] and/or /// [`Self::emoji`] after this. - pub fn new(custom_id: impl Into) -> Self { - Self(Button { + pub fn new(custom_id: impl Into>) -> Self { + Self { kind: ComponentType::Button, - data: ButtonKind::NonLink { - style: ButtonStyle::Primary, - custom_id: custom_id.into().into(), - }, + style: ButtonStyle::Primary, + custom_id: Some(custom_id.into()), + url: None, label: None, emoji: None, disabled: false, - }) + } } /// Sets the custom id of the button, a developer-defined identifier. Replaces the current /// value as set in [`Self::new`]. /// /// Has no effect on link buttons. - pub fn custom_id(mut self, id: impl Into) -> Self { - if let ButtonKind::NonLink { - custom_id, .. - } = &mut self.0.data - { - *custom_id = id.into().into(); + pub fn custom_id(mut self, id: impl Into>) -> Self { + if self.url.is_none() { + self.custom_id = Some(id.into()); } + self } @@ -86,30 +99,28 @@ impl CreateButton { /// /// Has no effect on link buttons. pub fn style(mut self, new_style: ButtonStyle) -> Self { - if let ButtonKind::NonLink { - style, .. - } = &mut self.0.data - { - *style = new_style; + if self.url.is_none() { + self.style = new_style; } + self } /// Sets label of the button. - pub fn label(mut self, label: impl Into) -> Self { - self.0.label = Some(label.into().into()); + pub fn label(mut self, label: impl Into>) -> Self { + self.label = Some(label.into()); self } /// Sets emoji of the button. pub fn emoji(mut self, emoji: impl Into) -> Self { - self.0.emoji = Some(emoji.into()); + self.emoji = Some(emoji.into()); self } /// Sets the disabled state for the button. pub fn disabled(mut self, disabled: bool) -> Self { - self.0.disabled = disabled; + self.disabled = disabled; self } } @@ -129,33 +140,50 @@ impl Serialize for CreateSelectMenuDefault { /// [Discord docs](https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure). #[derive(Clone, Debug, PartialEq)] -pub enum CreateSelectMenuKind { - String { options: Vec }, - User { default_users: Option> }, - Role { default_roles: Option> }, - Mentionable { default_users: Option>, default_roles: Option> }, - Channel { channel_types: Option>, default_channels: Option> }, +pub enum CreateSelectMenuKind<'a> { + String { + options: Cow<'a, [CreateSelectMenuOption<'a>]>, + }, + User { + default_users: Option>, + }, + Role { + default_roles: Option>, + }, + Mentionable { + default_users: Option>, + default_roles: Option>, + }, + Channel { + channel_types: Option>, + default_channels: Option>, + }, } -impl Serialize for CreateSelectMenuKind { +impl<'a> Serialize for CreateSelectMenuKind<'a> { fn serialize(&self, serializer: S) -> Result { #[derive(Serialize)] struct Json<'a> { #[serde(rename = "type")] kind: u8, #[serde(skip_serializing_if = "Option::is_none")] - options: Option<&'a [CreateSelectMenuOption]>, + options: Option<&'a [CreateSelectMenuOption<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] channel_types: Option<&'a [ChannelType]>, - #[serde(skip_serializing_if = "Vec::is_empty")] - default_values: Vec, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + default_values: &'a [CreateSelectMenuDefault], } - fn map + Copy>( - values: &Option>, - ) -> impl Iterator + '_ { + fn map<'a>( + values: &'a Option + Copy]>>, + ) -> impl Iterator + 'a { // Calling `.iter().flatten()` on the `Option` treats `None` like an empty vec - values.iter().flatten().map(|&i| CreateSelectMenuDefault(i.into())) + values + .as_ref() + .map(|s| s.iter()) + .into_iter() + .flatten() + .map(|&i| CreateSelectMenuDefault(i.into())) } #[rustfmt::skip] @@ -188,7 +216,7 @@ impl Serialize for CreateSelectMenuKind { Self::Channel { channel_types, default_channels: _ } => channel_types.as_deref(), _ => None, }, - default_values, + default_values: &default_values, }; json.serialize(serializer) @@ -200,10 +228,10 @@ impl Serialize for CreateSelectMenuKind { /// [Discord docs](https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure). #[derive(Clone, Debug, Serialize, PartialEq)] #[must_use] -pub struct CreateSelectMenu { - custom_id: String, +pub struct CreateSelectMenu<'a> { + custom_id: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] - placeholder: Option, + placeholder: Option>, #[serde(skip_serializing_if = "Option::is_none")] min_values: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -212,13 +240,13 @@ pub struct CreateSelectMenu { disabled: Option, #[serde(flatten)] - kind: CreateSelectMenuKind, + kind: CreateSelectMenuKind<'a>, } -impl CreateSelectMenu { +impl<'a> CreateSelectMenu<'a> { /// Creates a builder with given custom id (a developer-defined identifier), and a list of /// options, leaving all other fields empty. - pub fn new(custom_id: impl Into, kind: CreateSelectMenuKind) -> Self { + pub fn new(custom_id: impl Into>, kind: CreateSelectMenuKind<'a>) -> Self { Self { custom_id: custom_id.into(), placeholder: None, @@ -230,14 +258,14 @@ impl CreateSelectMenu { } /// The placeholder of the select menu. - pub fn placeholder(mut self, label: impl Into) -> Self { + pub fn placeholder(mut self, label: impl Into>) -> Self { self.placeholder = Some(label.into()); self } /// Sets the custom id of the select menu, a developer-defined identifier. Replaces the current /// value as set in [`Self::new`]. - pub fn custom_id(mut self, id: impl Into) -> Self { + pub fn custom_id(mut self, id: impl Into>) -> Self { self.custom_id = id.into(); self } @@ -266,21 +294,21 @@ impl CreateSelectMenu { /// [Discord docs](https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-option-structure) #[derive(Clone, Debug, Serialize, PartialEq)] #[must_use] -pub struct CreateSelectMenuOption { - label: String, - value: String, +pub struct CreateSelectMenuOption<'a> { + label: Cow<'a, str>, + value: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, #[serde(skip_serializing_if = "Option::is_none")] emoji: Option, #[serde(skip_serializing_if = "Option::is_none")] default: Option, } -impl CreateSelectMenuOption { +impl<'a> CreateSelectMenuOption<'a> { /// Creates a select menu option with the given label and value, leaving all other fields /// empty. - pub fn new(label: impl Into, value: impl Into) -> Self { + pub fn new(label: impl Into>, value: impl Into>) -> Self { Self { label: label.into(), value: value.into(), @@ -291,19 +319,19 @@ impl CreateSelectMenuOption { } /// Sets the label of this option, replacing the current value as set in [`Self::new`]. - pub fn label(mut self, label: impl Into) -> Self { + pub fn label(mut self, label: impl Into>) -> Self { self.label = label.into(); self } /// Sets the value of this option, replacing the current value as set in [`Self::new`]. - pub fn value(mut self, value: impl Into) -> Self { + pub fn value(mut self, value: impl Into>) -> Self { self.value = value.into(); self } /// Sets the description shown on this option. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -326,20 +354,33 @@ impl CreateSelectMenuOption { /// [Discord docs](https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure). #[derive(Clone, Debug, Serialize, PartialEq)] #[must_use] -pub struct CreateInputText(InputText); +pub struct CreateInputText<'a> { + #[serde(rename = "type")] + kind: ComponentType, + custom_id: Cow<'a, str>, + style: InputTextStyle, + label: Option>, + min_length: Option, + max_length: Option, + required: bool, + #[serde(skip_serializing_if = "Option::is_none")] + value: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + placeholder: Option>, +} -impl CreateInputText { +impl<'a> CreateInputText<'a> { /// Creates a text input with the given style, label, and custom id (a developer-defined /// identifier), leaving all other fields empty. pub fn new( style: InputTextStyle, - label: impl Into, - custom_id: impl Into, + label: impl Into>, + custom_id: impl Into>, ) -> Self { - Self(InputText { - style: Some(style), - label: Some(label.into().into()), - custom_id: custom_id.into().into(), + Self { + style, + label: Some(label.into()), + custom_id: custom_id.into(), placeholder: None, min_length: None, @@ -348,55 +389,55 @@ impl CreateInputText { required: true, kind: ComponentType::InputText, - }) + } } /// Sets the style of this input text. Replaces the current value as set in [`Self::new`]. pub fn style(mut self, kind: InputTextStyle) -> Self { - self.0.style = Some(kind); + self.style = kind; self } /// Sets the label of this input text. Replaces the current value as set in [`Self::new`]. - pub fn label(mut self, label: impl Into) -> Self { - self.0.label = Some(label.into().into()); + pub fn label(mut self, label: impl Into>) -> Self { + self.label = Some(label.into()); self } /// Sets the custom id of the input text, a developer-defined identifier. Replaces the current /// value as set in [`Self::new`]. - pub fn custom_id(mut self, id: impl Into) -> Self { - self.0.custom_id = id.into().into(); + pub fn custom_id(mut self, id: impl Into>) -> Self { + self.custom_id = id.into(); self } /// Sets the placeholder of this input text. - pub fn placeholder(mut self, label: impl Into) -> Self { - self.0.placeholder = Some(label.into().into()); + pub fn placeholder(mut self, label: impl Into>) -> Self { + self.placeholder = Some(label.into()); self } /// Sets the minimum length required for the input text pub fn min_length(mut self, min: u16) -> Self { - self.0.min_length = Some(min); + self.min_length = Some(min); self } /// Sets the maximum length required for the input text pub fn max_length(mut self, max: u16) -> Self { - self.0.max_length = Some(max); + self.max_length = Some(max); self } /// Sets the value of this input text. - pub fn value(mut self, value: impl Into) -> Self { - self.0.value = Some(value.into().into()); + pub fn value(mut self, value: impl Into>) -> Self { + self.value = Some(value.into()); self } /// Sets if the input text is required pub fn required(mut self, required: bool) -> Self { - self.0.required = required; + self.required = required; self } } diff --git a/src/builder/create_embed.rs b/src/builder/create_embed.rs index ba968673f54..07445a8411a 100644 --- a/src/builder/create_embed.rs +++ b/src/builder/create_embed.rs @@ -14,6 +14,8 @@ //! [`ExecuteWebhook::embeds`]: crate::builder::ExecuteWebhook::embeds //! [here]: https://discord.com/developers/docs/resources/channel#embed-object +use std::borrow::Cow; + #[cfg(feature = "http")] use crate::internal::prelude::*; use crate::model::prelude::*; @@ -21,24 +23,38 @@ use crate::model::prelude::*; /// A builder to create an embed in a message /// /// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object) -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize)] #[must_use] -pub struct CreateEmbed { - inner: Embed, - fields: Vec, -} - -impl serde::Serialize for CreateEmbed { - fn serialize(&self, serializer: S) -> StdResult { - let owned_self = self.clone(); - - let mut embed = owned_self.inner; - embed.fields = owned_self.fields.into(); - embed.serialize(serializer) - } +pub struct CreateEmbed<'a> { + #[serde(skip_serializing_if = "Option::is_none")] + title: Option>, + #[serde(rename = "type")] + #[serde(skip_serializing_if = "Option::is_none")] + kind: Option<&'static str>, + #[serde(skip_serializing_if = "Option::is_none")] + description: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + url: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + timestamp: Option, + #[serde(rename = "color")] + #[serde(skip_serializing_if = "Option::is_none")] + colour: Option, + #[serde(skip_serializing_if = "Option::is_none")] + footer: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + image: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + thumbnail: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + author: Option>, + /// No point using a Cow slice, as there is no set_fields method + /// and CreateEmbedField is not public. + #[serde(skip_serializing_if = "<[_]>::is_empty")] + fields: Vec>, } -impl CreateEmbed { +impl<'a> CreateEmbed<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -47,8 +63,8 @@ impl CreateEmbed { /// Set the author of the embed. /// /// Refer to the documentation for [`CreateEmbedAuthor`] for more information. - pub fn author(mut self, author: CreateEmbedAuthor) -> Self { - self.inner.author = Some(author.0); + pub fn author(mut self, author: CreateEmbedAuthor<'a>) -> Self { + self.author = Some(author); self } @@ -63,7 +79,7 @@ impl CreateEmbed { /// Set the colour of the left-hand side of the embed. #[inline] pub fn colour>(mut self, colour: C) -> Self { - self.inner.colour = Some(colour.into()); + self.colour = Some(colour.into()); self } @@ -71,8 +87,8 @@ impl CreateEmbed { /// /// **Note**: This can't be longer than 4096 characters. #[inline] - pub fn description(mut self, description: impl Into) -> Self { - self.inner.description = Some(description.into().into()); + pub fn description(mut self, description: impl Into>) -> Self { + self.description = Some(description.into()); self } @@ -83,11 +99,15 @@ impl CreateEmbed { #[inline] pub fn field( mut self, - name: impl Into, - value: impl Into, + name: impl Into>, + value: impl Into>, inline: bool, ) -> Self { - self.fields.push(EmbedField::new(name, value, inline)); + self.fields.push(CreateEmbedField { + name: name.into(), + value: value.into(), + inline, + }); self } @@ -96,11 +116,15 @@ impl CreateEmbed { /// This is sugar to reduce the need of calling [`Self::field`] manually multiple times. pub fn fields(mut self, fields: impl IntoIterator) -> Self where - N: Into, - V: Into, + N: Into>, + V: Into>, { - let fields = - fields.into_iter().map(|(name, value, inline)| EmbedField::new(name, value, inline)); + let fields = fields.into_iter().map(|(name, value, inline)| CreateEmbedField { + name: name.into(), + value: value.into(), + inline, + }); + self.fields.extend(fields); self } @@ -108,31 +132,25 @@ impl CreateEmbed { /// Set the footer of the embed. /// /// Refer to the documentation for [`CreateEmbedFooter`] for more information. - pub fn footer(mut self, footer: CreateEmbedFooter) -> Self { - self.inner.footer = Some(footer.0); + pub fn footer(mut self, footer: CreateEmbedFooter<'a>) -> Self { + self.footer = Some(footer); self } /// Set the image associated with the embed. This only supports HTTP(S). #[inline] - pub fn image(mut self, url: impl Into) -> Self { - self.inner.image = Some(EmbedImage { - url: url.into().into(), - proxy_url: None, - height: None, - width: None, + pub fn image(mut self, url: impl Into>) -> Self { + self.image = Some(CreateEmbedImage { + url: url.into(), }); self } /// Set the thumbnail of the embed. This only supports HTTP(S). #[inline] - pub fn thumbnail(mut self, url: impl Into) -> Self { - self.inner.thumbnail = Some(EmbedThumbnail { - url: url.into().into(), - proxy_url: None, - height: None, - width: None, + pub fn thumbnail(mut self, url: impl Into>) -> Self { + self.thumbnail = Some(CreateEmbedImage { + url: url.into(), }); self } @@ -153,21 +171,21 @@ impl CreateEmbed { /// ``` #[inline] pub fn timestamp>(mut self, timestamp: T) -> Self { - self.inner.timestamp = Some(timestamp.into()); + self.timestamp = Some(timestamp.into()); self } /// Set the title of the embed. #[inline] - pub fn title(mut self, title: impl Into) -> Self { - self.inner.title = Some(title.into().into()); + pub fn title(mut self, title: impl Into>) -> Self { + self.title = Some(title.into()); self } /// Set the URL to direct to when clicking on the title. #[inline] - pub fn url(mut self, url: impl Into) -> Self { - self.inner.url = Some(url.into().into()); + pub fn url(mut self, url: impl Into>) -> Self { + self.url = Some(url.into()); self } @@ -187,11 +205,11 @@ impl CreateEmbed { #[cfg(feature = "http")] pub(super) fn check_length(&self) -> Result<()> { let mut length = 0; - if let Some(ref author) = self.inner.author { + if let Some(ref author) = self.author { length += author.name.chars().count(); } - if let Some(ref description) = self.inner.description { + if let Some(ref description) = self.description { length += description.chars().count(); } @@ -200,11 +218,11 @@ impl CreateEmbed { length += field.value.chars().count(); } - if let Some(ref footer) = self.inner.footer { + if let Some(ref footer) = self.footer { length += footer.text.chars().count(); } - if let Some(ref title) = self.inner.title { + if let Some(ref title) = self.title { length += title.chars().count(); } @@ -213,83 +231,93 @@ impl CreateEmbed { } } -impl Default for CreateEmbed { +impl<'a> Default for CreateEmbed<'a> { /// Creates a builder with default values, setting the `type` to `rich`. fn default() -> Self { Self { fields: Vec::new(), - inner: Embed { - fields: FixedArray::new(), - description: None, - thumbnail: None, - timestamp: None, - kind: Some("rich".to_string().into()), - author: None, - colour: None, - footer: None, - image: None, - title: None, - url: None, - video: None, - provider: None, - }, + description: None, + thumbnail: None, + timestamp: None, + kind: Some("rich"), + author: None, + colour: None, + footer: None, + image: None, + title: None, + url: None, } } } -impl From for CreateEmbed { - fn from(mut embed: Embed) -> Self { +impl<'a> From for CreateEmbed<'a> { + fn from(embed: Embed) -> Self { Self { - fields: std::mem::take(&mut embed.fields).into_vec(), - inner: embed, + fields: embed.fields.into_iter().map(Into::into).collect(), + description: embed.description.map(FixedString::into_string).map(Into::into), + thumbnail: embed.thumbnail.map(Into::into), + timestamp: embed.timestamp, + kind: Some("rich"), + author: embed.author.map(Into::into), + colour: embed.colour, + footer: embed.footer.map(Into::into), + image: embed.image.map(Into::into), + title: embed.title.map(FixedString::into_string).map(Into::into), + url: embed.url.map(FixedString::into_string).map(Into::into), } } } /// A builder to create the author data of an emebd. See [`CreateEmbed::author`] -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, PartialEq, Serialize)] #[must_use] -pub struct CreateEmbedAuthor(EmbedAuthor); +pub struct CreateEmbedAuthor<'a> { + name: Cow<'a, str>, + url: Option>, + icon_url: Option>, +} -impl CreateEmbedAuthor { +impl<'a> CreateEmbedAuthor<'a> { /// Creates an author object with the given name, leaving all other fields empty. - pub fn new(name: impl Into) -> Self { - Self(EmbedAuthor { - name: name.into().into(), + pub fn new(name: impl Into>) -> Self { + Self { + name: name.into(), icon_url: None, url: None, - // Has no builder method because I think this field is only relevant when receiving (?) - proxy_icon_url: None, - }) + } } /// Set the author's name, replacing the current value as set in [`Self::new`]. - pub fn name(mut self, name: impl Into) -> Self { - self.0.name = name.into().into(); + pub fn name(mut self, name: impl Into>) -> Self { + self.name = name.into(); self } /// Set the URL of the author's icon. - pub fn icon_url(mut self, icon_url: impl Into) -> Self { - self.0.icon_url = Some(icon_url.into().into()); + pub fn icon_url(mut self, icon_url: impl Into>) -> Self { + self.icon_url = Some(icon_url.into()); self } /// Set the author's URL. - pub fn url(mut self, url: impl Into) -> Self { - self.0.url = Some(url.into().into()); + pub fn url(mut self, url: impl Into>) -> Self { + self.url = Some(url.into()); self } } -impl From for CreateEmbedAuthor { +impl<'a> From for CreateEmbedAuthor<'a> { fn from(author: EmbedAuthor) -> Self { - Self(author) + Self { + name: author.name.into_string().into(), + url: author.url.map(|f| f.into_string().into()), + icon_url: author.icon_url.map(|f| f.into_string().into()), + } } } #[cfg(feature = "model")] -impl From for CreateEmbedAuthor { +impl From for CreateEmbedAuthor<'_> { fn from(user: User) -> Self { let avatar_icon = user.face(); Self::new(user.name).icon_url(avatar_icon) @@ -297,36 +325,88 @@ impl From for CreateEmbedAuthor { } /// A builder to create the footer data for an embed. See [`CreateEmbed::footer`] -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, PartialEq, Serialize)] #[must_use] -pub struct CreateEmbedFooter(EmbedFooter); +pub struct CreateEmbedFooter<'a> { + text: Cow<'a, str>, + icon_url: Option>, +} -impl CreateEmbedFooter { +impl<'a> CreateEmbedFooter<'a> { /// Creates a new footer object with the given text, leaving all other fields empty. - pub fn new(text: impl Into) -> Self { - Self(EmbedFooter { - text: text.into().into(), + pub fn new(text: impl Into>) -> Self { + Self { + text: text.into(), icon_url: None, - // Has no builder method because I think this field is only relevant when receiving (?) - proxy_icon_url: None, - }) + } } /// Set the footer's text, replacing the current value as set in [`Self::new`]. - pub fn text(mut self, text: impl Into) -> Self { - self.0.text = text.into().into(); + pub fn text(mut self, text: impl Into>) -> Self { + self.text = text.into(); self } /// Set the icon URL's value. This only supports HTTP(S). - pub fn icon_url(mut self, icon_url: impl Into) -> Self { - self.0.icon_url = Some(icon_url.into().into()); + pub fn icon_url(mut self, icon_url: impl Into>) -> Self { + self.icon_url = Some(icon_url.into()); self } } -impl From for CreateEmbedFooter { +impl<'a> From for CreateEmbedFooter<'a> { fn from(footer: EmbedFooter) -> Self { - Self(footer) + Self { + text: footer.text.into_string().into(), + icon_url: footer.icon_url.map(|f| f.into_string().into()), + } + } +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +struct CreateEmbedField<'a> { + name: Cow<'a, str>, + value: Cow<'a, str>, + inline: bool, +} + +impl<'a> From<&'a EmbedField> for CreateEmbedField<'a> { + fn from(field: &'a EmbedField) -> Self { + Self { + name: field.name.as_str().into(), + value: field.value.as_str().into(), + inline: field.inline, + } + } +} + +impl<'a> From for CreateEmbedField<'a> { + fn from(field: EmbedField) -> Self { + Self { + name: field.name.into_string().into(), + value: field.value.into_string().into(), + inline: field.inline, + } + } +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +struct CreateEmbedImage<'a> { + url: Cow<'a, str>, +} + +impl<'a> From for CreateEmbedImage<'a> { + fn from(field: EmbedImage) -> Self { + Self { + url: field.url.into_string().into(), + } + } +} + +impl<'a> From for CreateEmbedImage<'a> { + fn from(field: EmbedThumbnail) -> Self { + Self { + url: field.url.into_string().into(), + } } } diff --git a/src/builder/create_forum_post.rs b/src/builder/create_forum_post.rs index 8a0f6872593..25fb0736172 100644 --- a/src/builder/create_forum_post.rs +++ b/src/builder/create_forum_post.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateMessage; @@ -11,14 +13,14 @@ use crate::model::prelude::*; #[derive(Clone, Debug, Serialize)] #[must_use] pub struct CreateForumPost<'a> { - name: String, + name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] auto_archive_duration: Option, #[serde(skip_serializing_if = "Option::is_none")] rate_limit_per_user: Option, - message: CreateMessage, - #[serde(skip_serializing_if = "Vec::is_empty")] - applied_tags: Vec, + message: CreateMessage<'a>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + applied_tags: Cow<'a, [ForumTagId]>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -26,13 +28,13 @@ pub struct CreateForumPost<'a> { impl<'a> CreateForumPost<'a> { /// Creates a builder with the given name and message content, leaving all other fields empty. - pub fn new(name: impl Into, message: CreateMessage) -> Self { + pub fn new(name: impl Into>, message: CreateMessage<'a>) -> Self { Self { name: name.into(), message, auto_archive_duration: None, rate_limit_per_user: None, - applied_tags: Vec::new(), + applied_tags: Cow::default(), audit_log_reason: None, } } @@ -40,7 +42,7 @@ impl<'a> CreateForumPost<'a> { /// The name of the forum post. Replaces the current value as set in [`Self::new`]. /// /// **Note**: Must be between 2 and 100 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } @@ -48,7 +50,7 @@ impl<'a> CreateForumPost<'a> { /// The contents of the first message in the forum post. /// /// See [`CreateMessage`] for restrictions around message size. - pub fn message(mut self, message: CreateMessage) -> Self { + pub fn message(mut self, message: CreateMessage<'a>) -> Self { self.message = message; self } @@ -75,15 +77,12 @@ impl<'a> CreateForumPost<'a> { } pub fn add_applied_tag(mut self, applied_tag: ForumTagId) -> Self { - self.applied_tags.push(applied_tag); + self.applied_tags.to_mut().push(applied_tag); self } - pub fn set_applied_tags( - mut self, - applied_tags: impl IntoIterator>, - ) -> Self { - self.applied_tags = applied_tags.into_iter().map(Into::into).collect(); + pub fn set_applied_tags(mut self, applied_tags: impl Into>) -> Self { + self.applied_tags = applied_tags.into(); self } diff --git a/src/builder/create_forum_tag.rs b/src/builder/create_forum_tag.rs index d616b3e2481..bdb6fe98df4 100644 --- a/src/builder/create_forum_tag.rs +++ b/src/builder/create_forum_tag.rs @@ -1,4 +1,5 @@ -use crate::internal::prelude::*; +use std::borrow::Cow; + use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/channel#forum-tag-object-forum-tag-structure) @@ -6,17 +7,17 @@ use crate::model::prelude::*; /// Contrary to the [`ForumTag`] struct, only the name field is required. #[must_use] #[derive(Clone, Debug, Serialize)] -pub struct CreateForumTag { - name: FixedString, +pub struct CreateForumTag<'a> { + name: Cow<'a, str>, moderated: bool, emoji_id: Option, - emoji_name: Option, + emoji_name: Option>, } -impl CreateForumTag { - pub fn new(name: impl Into) -> Self { +impl<'a> CreateForumTag<'a> { + pub fn new(name: impl Into>) -> Self { Self { - name: name.into().into(), + name: name.into(), moderated: false, emoji_id: None, emoji_name: None, @@ -38,7 +39,7 @@ impl CreateForumTag { }, ReactionType::Unicode(unicode_emoji) => { self.emoji_id = None; - self.emoji_name = Some(unicode_emoji); + self.emoji_name = Some(unicode_emoji.into_string().into()); }, } self diff --git a/src/builder/create_interaction_response.rs b/src/builder/create_interaction_response.rs index 6d645c150d3..b82aa0ea79c 100644 --- a/src/builder/create_interaction_response.rs +++ b/src/builder/create_interaction_response.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::{check_overflow, Builder}; use super::{ @@ -17,7 +19,7 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object). #[derive(Clone, Debug)] -pub enum CreateInteractionResponse { +pub enum CreateInteractionResponse<'a> { /// Acknowledges a Ping (only required when your bot uses an HTTP endpoint URL). /// /// Corresponds to Discord's `PONG`. @@ -25,12 +27,12 @@ pub enum CreateInteractionResponse { /// Responds to an interaction with a message. /// /// Corresponds to Discord's `CHANNEL_MESSAGE_WITH_SOURCE`. - Message(CreateInteractionResponseMessage), + Message(CreateInteractionResponseMessage<'a>), /// Acknowledges the interaction in order to edit a response later. The user sees a loading /// state. /// /// Corresponds to Discord's `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`. - Defer(CreateInteractionResponseMessage), + Defer(CreateInteractionResponseMessage<'a>), /// Only valid for component-based interactions (seems to work for modal submit interactions /// too even though it's not documented). /// @@ -44,19 +46,19 @@ pub enum CreateInteractionResponse { /// Edits the message the component was attached to. /// /// Corresponds to Discord's `UPDATE_MESSAGE`. - UpdateMessage(CreateInteractionResponseMessage), + UpdateMessage(CreateInteractionResponseMessage<'a>), /// Only valid for autocomplete interactions. /// /// Responds to the autocomplete interaction with suggested choices. /// /// Corresponds to Discord's `APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`. - Autocomplete(CreateAutocompleteResponse), + Autocomplete(CreateAutocompleteResponse<'a>), /// Not valid for Modal and Ping interactions /// /// Responds to the interaction with a popup modal. /// /// Corresponds to Discord's `MODAL`. - Modal(CreateModal), + Modal(CreateModal<'a>), /// Not valid for autocomplete and Ping interactions. Only available for applications with /// monetization enabled. /// @@ -66,7 +68,7 @@ pub enum CreateInteractionResponse { PremiumRequired, } -impl serde::Serialize for CreateInteractionResponse { +impl serde::Serialize for CreateInteractionResponse<'_> { fn serialize(&self, serializer: S) -> StdResult { use serde::ser::Error as _; @@ -97,7 +99,7 @@ impl serde::Serialize for CreateInteractionResponse { } } -impl CreateInteractionResponse { +impl CreateInteractionResponse<'_> { #[cfg(feature = "http")] fn check_length(&self) -> Result<()> { if let CreateInteractionResponse::Message(data) @@ -113,7 +115,7 @@ impl CreateInteractionResponse { check_overflow(embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in embeds { + for embed in embeds.iter() { embed.check_length()?; } } @@ -124,7 +126,7 @@ impl CreateInteractionResponse { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for CreateInteractionResponse { +impl Builder for CreateInteractionResponse<'_> { type Context<'ctx> = (InteractionId, &'ctx str); type Built = (); @@ -165,23 +167,23 @@ impl Builder for CreateInteractionResponse { /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-messages). #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct CreateInteractionResponseMessage { +pub struct CreateInteractionResponseMessage<'a> { #[serde(skip_serializing_if = "Option::is_none")] tts: Option, #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, #[serde(skip_serializing_if = "Option::is_none")] - embeds: Option>, + embeds: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, #[serde(skip_serializing_if = "Option::is_none")] - components: Option>, - attachments: EditAttachments, + components: Option]>>, + attachments: EditAttachments<'a>, } -impl CreateInteractionResponseMessage { +impl<'a> CreateInteractionResponseMessage<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -198,13 +200,13 @@ impl CreateInteractionResponseMessage { } /// Appends a file to the message. - pub fn add_file(mut self, file: CreateAttachment) -> Self { + pub fn add_file(mut self, file: CreateAttachment<'a>) -> Self { self.attachments = self.attachments.add(file); self } /// Appends a list of files to the message. - pub fn add_files(mut self, files: impl IntoIterator) -> Self { + pub fn add_files(mut self, files: impl IntoIterator>) -> Self { for file in files { self.attachments = self.attachments.add(file); } @@ -215,7 +217,7 @@ impl CreateInteractionResponseMessage { /// /// Calling this multiple times will overwrite the file list. To append files, call /// [`Self::add_file`] or [`Self::add_files`] instead. - pub fn files(mut self, files: impl IntoIterator) -> Self { + pub fn files(mut self, files: impl IntoIterator>) -> Self { self.attachments = EditAttachments::new(); self.add_files(files) } @@ -224,7 +226,7 @@ impl CreateInteractionResponseMessage { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -232,16 +234,16 @@ impl CreateInteractionResponseMessage { /// Adds an embed to the message. /// /// Calling this while editing a message will overwrite existing embeds. - pub fn add_embed(mut self, embed: CreateEmbed) -> Self { - self.embeds.get_or_insert_with(Vec::new).push(embed); + pub fn add_embed(mut self, embed: CreateEmbed<'a>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().push(embed); self } /// Adds multiple embeds for the message. /// /// Calling this while editing a message will overwrite existing embeds. - pub fn add_embeds(mut self, embeds: Vec) -> Self { - self.embeds.get_or_insert_with(Vec::new).extend(embeds); + pub fn add_embeds(mut self, embeds: impl IntoIterator>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().extend(embeds); self } @@ -249,7 +251,7 @@ impl CreateInteractionResponseMessage { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embed`] /// instead. - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { self.embeds(vec![embed]) } @@ -257,13 +259,13 @@ impl CreateInteractionResponseMessage { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embeds`] /// instead. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = Some(embeds); + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = Some(embeds.into()); self } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } @@ -289,8 +291,8 @@ impl CreateInteractionResponseMessage { } /// Sets the components of this message. - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); @@ -300,31 +302,35 @@ impl CreateInteractionResponseMessage { // [Autocomplete docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-autocomplete). #[must_use] #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(transparent)] -pub struct AutocompleteChoice(CommandOptionChoice); -impl AutocompleteChoice { - pub fn new(name: impl Into, value: impl Into) -> Self { - Self(CommandOptionChoice { - name: name.into().into(), +pub struct AutocompleteChoice<'a> { + pub name: Cow<'a, str>, + #[serde(skip_serializing_if = "Option::is_none")] + pub name_localizations: Option, Cow<'a, str>>>, + pub value: Value, +} + +impl<'a> AutocompleteChoice<'a> { + pub fn new(name: impl Into>, value: impl Into) -> Self { + Self { + name: name.into(), name_localizations: None, value: value.into(), - }) + } } pub fn add_localized_name( mut self, - locale: impl Into, - localized_name: impl Into, + locale: impl Into>, + localized_name: impl Into>, ) -> Self { - self.0 - .name_localizations + self.name_localizations .get_or_insert_with(Default::default) .insert(locale.into(), localized_name.into()); self } } -impl> From for AutocompleteChoice { +impl<'a, S: Into>> From for AutocompleteChoice<'a> { fn from(value: S) -> Self { let value = value.into(); let name = value.clone(); @@ -335,11 +341,11 @@ impl> From for AutocompleteChoice { /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-autocomplete) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct CreateAutocompleteResponse { - choices: Vec, +pub struct CreateAutocompleteResponse<'a> { + choices: Cow<'a, [AutocompleteChoice<'a>]>, } -impl CreateAutocompleteResponse { +impl<'a> CreateAutocompleteResponse<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -350,8 +356,8 @@ impl CreateAutocompleteResponse { /// See the official docs on [`Application Command Option Choices`] for more information. /// /// [`Application Command Option Choices`]: https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure - pub fn set_choices(mut self, choices: Vec) -> Self { - self.choices = choices; + pub fn set_choices(mut self, choices: impl Into]>>) -> Self { + self.choices = choices.into(); self } @@ -359,7 +365,7 @@ impl CreateAutocompleteResponse { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be between -2^53 and 2^53. - pub fn add_int_choice(self, name: impl Into, value: i64) -> Self { + pub fn add_int_choice(self, name: impl Into>, value: i64) -> Self { self.add_choice(AutocompleteChoice::new(name, value)) } @@ -367,7 +373,11 @@ impl CreateAutocompleteResponse { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be up to 100 characters. - pub fn add_string_choice(self, name: impl Into, value: impl Into) -> Self { + pub fn add_string_choice( + self, + name: impl Into>, + value: impl Into>, + ) -> Self { self.add_choice(AutocompleteChoice::new(name, value.into())) } @@ -375,19 +385,19 @@ impl CreateAutocompleteResponse { /// /// **Note**: There can be no more than 25 choices set. Name must be between 1 and 100 /// characters. Value must be between -2^53 and 2^53. - pub fn add_number_choice(self, name: impl Into, value: f64) -> Self { + pub fn add_number_choice(self, name: impl Into>, value: f64) -> Self { self.add_choice(AutocompleteChoice::new(name, value)) } - fn add_choice(mut self, value: AutocompleteChoice) -> Self { - self.choices.push(value); + fn add_choice(mut self, value: AutocompleteChoice<'a>) -> Self { + self.choices.to_mut().push(value); self } } #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for CreateAutocompleteResponse { +impl Builder for CreateAutocompleteResponse<'_> { type Context<'ctx> = (InteractionId, &'ctx str); type Built = (); @@ -408,17 +418,17 @@ impl Builder for CreateAutocompleteResponse { /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal). #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct CreateModal { - components: Vec, - custom_id: String, - title: String, +pub struct CreateModal<'a> { + components: Cow<'a, [CreateActionRow<'a>]>, + custom_id: Cow<'a, str>, + title: Cow<'a, str>, } -impl CreateModal { +impl<'a> CreateModal<'a> { /// Creates a new modal. - pub fn new(custom_id: impl Into, title: impl Into) -> Self { + pub fn new(custom_id: impl Into>, title: impl Into>) -> Self { Self { - components: Vec::new(), + components: Cow::default(), custom_id: custom_id.into(), title: title.into(), } @@ -427,8 +437,8 @@ impl CreateModal { /// Sets the components of this message. /// /// Overwrites existing components. - pub fn components(mut self, components: Vec) -> Self { - self.components = components; + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = components.into(); self } } diff --git a/src/builder/create_interaction_response_followup.rs b/src/builder/create_interaction_response_followup.rs index 153313ce21d..a23baa17be4 100644 --- a/src/builder/create_interaction_response_followup.rs +++ b/src/builder/create_interaction_response_followup.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::{check_overflow, Builder}; use super::{ @@ -18,24 +20,24 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#create-followup-message) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct CreateInteractionResponseFollowup { +pub struct CreateInteractionResponseFollowup<'a> { #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, // [Omitting username: not supported in interaction followups] // [Omitting avatar_url: not supported in interaction followups] #[serde(skip_serializing_if = "Option::is_none")] tts: Option, - embeds: Vec, + embeds: Cow<'a, [CreateEmbed<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] - components: Option>, + components: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, - attachments: EditAttachments, + attachments: EditAttachments<'a>, } -impl CreateInteractionResponseFollowup { +impl<'a> CreateInteractionResponseFollowup<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -50,7 +52,7 @@ impl CreateInteractionResponseFollowup { check_overflow(self.embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in &self.embeds { + for embed in self.embeds.iter() { embed.check_length()?; } @@ -61,7 +63,7 @@ impl CreateInteractionResponseFollowup { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -77,13 +79,13 @@ impl CreateInteractionResponseFollowup { } /// Appends a file to the message. - pub fn add_file(mut self, file: CreateAttachment) -> Self { + pub fn add_file(mut self, file: CreateAttachment<'a>) -> Self { self.attachments = self.attachments.add(file); self } /// Appends a list of files to the message. - pub fn add_files(mut self, files: impl IntoIterator) -> Self { + pub fn add_files(mut self, files: impl IntoIterator>) -> Self { for file in files { self.attachments = self.attachments.add(file); } @@ -94,20 +96,20 @@ impl CreateInteractionResponseFollowup { /// /// Calling this multiple times will overwrite the file list. To append files, call /// [`Self::add_file`] or [`Self::add_files`] instead. - pub fn files(mut self, files: impl IntoIterator) -> Self { + pub fn files(mut self, files: impl IntoIterator>) -> Self { self.attachments = EditAttachments::new(); self.add_files(files) } /// Adds an embed to the message. - pub fn add_embed(mut self, embed: CreateEmbed) -> Self { - self.embeds.push(embed); + pub fn add_embed(mut self, embed: CreateEmbed<'a>) -> Self { + self.embeds.to_mut().push(embed); self } /// Adds multiple embeds to the message. - pub fn add_embeds(mut self, embeds: Vec) -> Self { - self.embeds.extend(embeds); + pub fn add_embeds(mut self, embeds: impl IntoIterator>) -> Self { + self.embeds.to_mut().extend(embeds); self } @@ -115,7 +117,7 @@ impl CreateInteractionResponseFollowup { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embed`] /// instead. - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { self.embeds(vec![embed]) } @@ -123,13 +125,13 @@ impl CreateInteractionResponseFollowup { /// /// Calling this multiple times will overwrite the embed list. To append embeds, call /// [`Self::add_embeds`] instead. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = embeds; + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = embeds.into(); self } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } @@ -155,8 +157,8 @@ impl CreateInteractionResponseFollowup { } /// Sets the components of this message. - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); @@ -164,7 +166,7 @@ impl CreateInteractionResponseFollowup { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for CreateInteractionResponseFollowup { +impl Builder for CreateInteractionResponseFollowup<'_> { type Context<'ctx> = (Option, &'ctx str); type Built = Message; diff --git a/src/builder/create_message.rs b/src/builder/create_message.rs index 874ba3682ea..78687381678 100644 --- a/src/builder/create_message.rs +++ b/src/builder/create_message.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use super::create_poll::Ready; #[cfg(feature = "http")] use super::{check_overflow, Builder}; @@ -53,33 +55,33 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/channel#create-message) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct CreateMessage { +pub struct CreateMessage<'a> { #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, #[serde(skip_serializing_if = "Option::is_none")] nonce: Option, tts: bool, - embeds: Vec, + embeds: Cow<'a, [CreateEmbed<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] message_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] - components: Option>, - sticker_ids: Vec, + components: Option]>>, + sticker_ids: Cow<'a, [StickerId]>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, - pub(crate) attachments: EditAttachments, + pub(crate) attachments: EditAttachments<'a>, enforce_nonce: bool, #[serde(skip_serializing_if = "Option::is_none")] - poll: Option>, + poll: Option>, // The following fields are handled separately. #[serde(skip)] - reactions: Vec, + reactions: Cow<'a, [ReactionType]>, } -impl CreateMessage { +impl<'a> CreateMessage<'a> { pub fn new() -> Self { Self::default() } @@ -93,7 +95,7 @@ impl CreateMessage { check_overflow(self.embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in &self.embeds { + for embed in self.embeds.iter() { embed.check_length()?; } @@ -107,7 +109,7 @@ impl CreateMessage { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -116,8 +118,8 @@ impl CreateMessage { /// /// **Note**: This will keep all existing embeds. Use [`Self::embed()`] to replace existing /// embeds. - pub fn add_embed(mut self, embed: CreateEmbed) -> Self { - self.embeds.push(embed); + pub fn add_embed(mut self, embed: CreateEmbed<'a>) -> Self { + self.embeds.to_mut().push(embed); self } @@ -125,8 +127,8 @@ impl CreateMessage { /// /// **Note**: This will keep all existing embeds. Use [`Self::embeds()`] to replace existing /// embeds. - pub fn add_embeds(mut self, embeds: Vec) -> Self { - self.embeds.extend(embeds); + pub fn add_embeds(mut self, embeds: impl IntoIterator>) -> Self { + self.embeds.to_mut().extend(embeds); self } @@ -134,7 +136,7 @@ impl CreateMessage { /// /// **Note**: This will replace all existing embeds. Use [`Self::add_embed()`] to keep existing /// embeds. - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { self.embeds(vec![embed]) } @@ -142,8 +144,8 @@ impl CreateMessage { /// /// **Note**: This will replace all existing embeds. Use [`Self::add_embeds()`] to keep existing /// embeds. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = embeds; + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = embeds.into(); self } @@ -159,11 +161,8 @@ impl CreateMessage { /// Adds a list of reactions to create after the message's sent. #[inline] - pub fn reactions>( - mut self, - reactions: impl IntoIterator, - ) -> Self { - self.reactions = reactions.into_iter().map(Into::into).collect(); + pub fn reactions(mut self, reactions: impl Into>) -> Self { + self.reactions = reactions.into(); self } @@ -172,7 +171,7 @@ impl CreateMessage { /// **Note**: Requires the [Attach Files] permission. /// /// [Attach Files]: Permissions::ATTACH_FILES - pub fn add_file(mut self, file: CreateAttachment) -> Self { + pub fn add_file(mut self, file: CreateAttachment<'a>) -> Self { self.attachments = self.attachments.add(file); self } @@ -182,7 +181,7 @@ impl CreateMessage { /// **Note**: Requires the [Attach Files] permission. /// /// [Attach Files]: Permissions::ATTACH_FILES - pub fn add_files(mut self, files: impl IntoIterator) -> Self { + pub fn add_files(mut self, files: impl IntoIterator>) -> Self { for file in files { self.attachments = self.attachments.add(file); } @@ -197,13 +196,13 @@ impl CreateMessage { /// **Note**: Requires the [Attach Files] permission. /// /// [Attach Files]: Permissions::ATTACH_FILES - pub fn files(mut self, files: impl IntoIterator) -> Self { + pub fn files(mut self, files: impl IntoIterator>) -> Self { self.attachments = EditAttachments::new(); self.add_files(files) } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } @@ -215,8 +214,8 @@ impl CreateMessage { } /// Sets the components of this message. - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); @@ -241,11 +240,8 @@ impl CreateMessage { /// /// **Note**: This will replace all existing stickers. Use [`Self::add_sticker_id()`] or /// [`Self::add_sticker_ids()`] to keep existing stickers. - pub fn sticker_ids>( - mut self, - sticker_ids: impl IntoIterator, - ) -> Self { - self.sticker_ids = sticker_ids.into_iter().map(Into::into).collect(); + pub fn sticker_ids(mut self, sticker_ids: impl Into>) -> Self { + self.sticker_ids = sticker_ids.into(); self } @@ -256,7 +252,7 @@ impl CreateMessage { /// **Note**: This will keep all existing stickers. Use [`Self::sticker_id()`] to replace /// existing sticker. pub fn add_sticker_id(mut self, sticker_id: impl Into) -> Self { - self.sticker_ids.push(sticker_id.into()); + self.sticker_ids.to_mut().push(sticker_id.into()); self } @@ -270,9 +266,7 @@ impl CreateMessage { mut self, sticker_ids: impl IntoIterator, ) -> Self { - for sticker_id in sticker_ids { - self = self.add_sticker_id(sticker_id); - } + self.sticker_ids.to_mut().extend(sticker_ids.into_iter().map(Into::into)); self } @@ -294,7 +288,7 @@ impl CreateMessage { } /// Sets the [`Poll`] for this message. - pub fn poll(mut self, poll: CreatePoll) -> Self { + pub fn poll(mut self, poll: CreatePoll<'a, Ready>) -> Self { self.poll = Some(poll); self } @@ -302,7 +296,7 @@ impl CreateMessage { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for CreateMessage { +impl Builder for CreateMessage<'_> { type Context<'ctx> = (ChannelId, Option); type Built = Message; @@ -351,8 +345,8 @@ impl Builder for CreateMessage { #[cfg_attr(not(feature = "cache"), allow(unused_mut))] let mut message = http.send_message(channel_id, files, &self).await?; - for reaction in self.reactions { - http.create_reaction(channel_id, message.id, &reaction).await?; + for reaction in self.reactions.iter() { + http.create_reaction(channel_id, message.id, reaction).await?; } // HTTP sent Messages don't have guild_id set, so we fill it in ourselves by best effort diff --git a/src/builder/create_poll.rs b/src/builder/create_poll.rs index fc9ce629143..78f32a76640 100644 --- a/src/builder/create_poll.rs +++ b/src/builder/create_poll.rs @@ -1,4 +1,6 @@ -use crate::model::channel::{PollLayoutType, PollMedia, PollMediaEmoji}; +use std::borrow::Cow; + +use crate::model::channel::{PollLayoutType, PollMediaEmoji}; #[derive(serde::Serialize, Clone, Debug)] pub struct NeedsQuestion; @@ -24,15 +26,15 @@ use sealed::*; /// "Only text is supported." #[derive(serde::Serialize, Clone, Debug)] -struct CreatePollMedia { - text: String, +struct CreatePollMedia<'a> { + text: Cow<'a, str>, } #[derive(serde::Serialize, Clone, Debug)] #[must_use = "Builders do nothing unless built"] -pub struct CreatePoll { - question: CreatePollMedia, - answers: Vec, +pub struct CreatePoll<'a, Stage: Sealed> { + question: CreatePollMedia<'a>, + answers: Cow<'a, [CreatePollAnswer<'a>]>, duration: u8, allow_multiselect: bool, layout_type: Option, @@ -41,16 +43,16 @@ pub struct CreatePoll { _stage: Stage, } -impl Default for CreatePoll { +impl Default for CreatePoll<'_, NeedsQuestion> { /// See the documentation of [`Self::new`]. fn default() -> Self { // Producing dummy values is okay as we must transition through all `Stage`s before firing, // which fills in the values with real values. Self { question: CreatePollMedia { - text: String::default(), + text: Cow::default(), }, - answers: Vec::default(), + answers: Cow::default(), duration: u8::default(), allow_multiselect: false, layout_type: None, @@ -60,7 +62,7 @@ impl Default for CreatePoll { } } -impl CreatePoll { +impl<'a> CreatePoll<'a, NeedsQuestion> { /// Creates a builder for creating a Poll. /// /// This must be transitioned through in order, to provide all required fields. @@ -84,7 +86,7 @@ impl CreatePoll { } /// Sets the question to be polled. - pub fn question(self, text: impl Into) -> CreatePoll { + pub fn question(self, text: impl Into>) -> CreatePoll<'a, NeedsAnswers> { CreatePoll { question: CreatePollMedia { text: text.into(), @@ -98,12 +100,15 @@ impl CreatePoll { } } -impl CreatePoll { +impl<'a> CreatePoll<'a, NeedsAnswers> { /// Sets the answers that can be picked from. - pub fn answers(self, answers: Vec) -> CreatePoll { + pub fn answers( + self, + answers: impl Into]>>, + ) -> CreatePoll<'a, NeedsDuration> { CreatePoll { question: self.question, - answers, + answers: answers.into(), duration: self.duration, allow_multiselect: self.allow_multiselect, layout_type: self.layout_type, @@ -112,11 +117,11 @@ impl CreatePoll { } } -impl CreatePoll { +impl<'a> CreatePoll<'a, NeedsDuration> { /// Sets the duration for the Poll to run for. /// /// This must be less than a week, and will be rounded to hours towards zero. - pub fn duration(self, duration: std::time::Duration) -> CreatePoll { + pub fn duration(self, duration: std::time::Duration) -> CreatePoll<'a, Ready> { let hours = duration.as_secs() / 3600; CreatePoll { @@ -130,7 +135,7 @@ impl CreatePoll { } } -impl CreatePoll { +impl CreatePoll<'_, Stage> { /// Sets the layout type for the Poll to take. /// /// This is currently only ever [`PollLayoutType::Default`], and is optional. @@ -146,13 +151,19 @@ impl CreatePoll { } } +#[derive(serde::Serialize, Clone, Debug, Default)] +struct CreatePollAnswerMedia<'a> { + text: Option>, + emoji: Option, +} + #[derive(serde::Serialize, Clone, Debug, Default)] #[must_use = "Builders do nothing unless built"] -pub struct CreatePollAnswer { - poll_media: PollMedia, +pub struct CreatePollAnswer<'a> { + poll_media: CreatePollAnswerMedia<'a>, } -impl CreatePollAnswer { +impl<'a> CreatePollAnswer<'a> { /// Creates a builder for a Poll answer. /// /// [`Self::text`] or [`Self::emoji`] must be provided. @@ -160,7 +171,7 @@ impl CreatePollAnswer { Self::default() } - pub fn text(mut self, text: impl Into) -> Self { + pub fn text(mut self, text: impl Into>) -> Self { self.poll_media.text = Some(text.into()); self } diff --git a/src/builder/create_scheduled_event.rs b/src/builder/create_scheduled_event.rs index 938952588db..6a95910585a 100644 --- a/src/builder/create_scheduled_event.rs +++ b/src/builder/create_scheduled_event.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -14,14 +16,14 @@ pub struct CreateScheduledEvent<'a> { #[serde(skip_serializing_if = "Option::is_none")] channel_id: Option, #[serde(skip_serializing_if = "Option::is_none")] - entity_metadata: Option, - name: String, + entity_metadata: Option>, + name: Cow<'a, str>, privacy_level: ScheduledEventPrivacyLevel, - scheduled_start_time: String, + scheduled_start_time: Timestamp, #[serde(skip_serializing_if = "Option::is_none")] - scheduled_end_time: Option, + scheduled_end_time: Option, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, entity_type: ScheduledEventType, #[serde(skip_serializing_if = "Option::is_none")] image: Option, @@ -35,13 +37,13 @@ impl<'a> CreateScheduledEvent<'a> { /// empty. pub fn new( kind: ScheduledEventType, - name: impl Into, + name: impl Into>, scheduled_start_time: impl Into, ) -> Self { Self { name: name.into(), entity_type: kind, - scheduled_start_time: scheduled_start_time.into().to_string(), + scheduled_start_time: scheduled_start_time.into(), image: None, channel_id: None, @@ -66,13 +68,13 @@ impl<'a> CreateScheduledEvent<'a> { } /// Sets the name of the scheduled event, replacing the current value as set in [`Self::new`]. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } /// Sets the description of the scheduled event. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -80,14 +82,14 @@ impl<'a> CreateScheduledEvent<'a> { /// Sets the start time of the scheduled event, replacing the current value as set in /// [`Self::new`]. pub fn start_time(mut self, timestamp: impl Into) -> Self { - self.scheduled_start_time = timestamp.into().to_string(); + self.scheduled_start_time = timestamp.into(); self } /// Sets the end time of the scheduled event. Required if [`Self::kind`] is /// [`ScheduledEventType::External`]. pub fn end_time(mut self, timestamp: impl Into) -> Self { - self.scheduled_end_time = Some(timestamp.into().to_string()); + self.scheduled_end_time = Some(timestamp.into()); self } @@ -102,15 +104,15 @@ impl<'a> CreateScheduledEvent<'a> { /// [`Self::kind`] is [`ScheduledEventType::External`]. /// /// [`External`]: ScheduledEventType::External - pub fn location(mut self, location: impl Into) -> Self { - self.entity_metadata = Some(ScheduledEventMetadata { - location: Some(location.into().into()), + pub fn location(mut self, location: impl Into>) -> Self { + self.entity_metadata = Some(CreateScheduledEventMetadata { + location: Some(location.into()), }); self } /// Sets the cover image for the scheduled event. - pub fn image(mut self, image: &CreateAttachment) -> Self { + pub fn image(mut self, image: &CreateAttachment<'_>) -> Self { self.image = Some(image.to_base64()); self } @@ -149,3 +151,8 @@ impl<'a> Builder for CreateScheduledEvent<'a> { cache_http.http().create_scheduled_event(ctx, &self, self.audit_log_reason).await } } + +#[derive(Clone, Debug, Default, serde::Serialize)] +pub(crate) struct CreateScheduledEventMetadata<'a> { + pub(crate) location: Option>, +} diff --git a/src/builder/create_stage_instance.rs b/src/builder/create_stage_instance.rs index 9b3292d2c46..85d8015d9e7 100644 --- a/src/builder/create_stage_instance.rs +++ b/src/builder/create_stage_instance.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -13,7 +15,7 @@ use crate::model::prelude::*; #[must_use] pub struct CreateStageInstance<'a> { channel_id: Option, // required field, filled in Builder impl - topic: String, + topic: Cow<'a, str>, privacy_level: StageInstancePrivacyLevel, #[serde(skip_serializing_if = "Option::is_none")] send_start_notification: Option, @@ -24,7 +26,7 @@ pub struct CreateStageInstance<'a> { impl<'a> CreateStageInstance<'a> { /// Creates a builder with the provided topic. - pub fn new(topic: impl Into) -> Self { + pub fn new(topic: impl Into>) -> Self { Self { channel_id: None, topic: topic.into(), @@ -36,7 +38,7 @@ impl<'a> CreateStageInstance<'a> { /// Sets the topic of the stage channel instance, replacing the current value as set in /// [`Self::new`]. - pub fn topic(mut self, topic: impl Into) -> Self { + pub fn topic(mut self, topic: impl Into>) -> Self { self.topic = topic.into(); self } diff --git a/src/builder/create_sticker.rs b/src/builder/create_sticker.rs index 068fac365fa..e95cdbe80a4 100644 --- a/src/builder/create_sticker.rs +++ b/src/builder/create_sticker.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -14,20 +16,20 @@ use crate::model::prelude::*; #[derive(Clone, Debug)] #[must_use] pub struct CreateSticker<'a> { - name: String, - description: String, - tags: String, - file: CreateAttachment, + name: Cow<'static, str>, + description: Cow<'static, str>, + tags: Cow<'static, str>, + file: CreateAttachment<'a>, audit_log_reason: Option<&'a str>, } impl<'a> CreateSticker<'a> { /// Creates a new builder with the given data. All of this builder's fields are required. - pub fn new(name: impl Into, file: CreateAttachment) -> Self { + pub fn new(name: impl Into>, file: CreateAttachment<'a>) -> Self { Self { name: name.into(), - tags: String::new(), - description: String::new(), + tags: Cow::default(), + description: Cow::default(), file, audit_log_reason: None, } @@ -36,7 +38,7 @@ impl<'a> CreateSticker<'a> { /// Set the name of the sticker, replacing the current value as set in [`Self::new`]. /// /// **Note**: Must be between 2 and 30 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } @@ -44,7 +46,7 @@ impl<'a> CreateSticker<'a> { /// Set the description of the sticker. /// /// **Note**: Must be empty or 2-100 characters. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = description.into(); self } @@ -52,7 +54,7 @@ impl<'a> CreateSticker<'a> { /// The Discord name of a unicode emoji representing the sticker's expression. /// /// **Note**: Max 200 characters long. - pub fn tags(mut self, tags: impl Into) -> Self { + pub fn tags(mut self, tags: impl Into>) -> Self { self.tags = tags.into(); self } @@ -60,7 +62,7 @@ impl<'a> CreateSticker<'a> { /// Set the sticker file. Replaces the current value as set in [`Self::new`]. /// /// **Note**: Must be a PNG, APNG, or Lottie JSON file, max 500 KB. - pub fn file(mut self, file: CreateAttachment) -> Self { + pub fn file(mut self, file: CreateAttachment<'a>) -> Self { self.file = file; self } diff --git a/src/builder/create_thread.rs b/src/builder/create_thread.rs index 4358d78c9f9..5547d6c7f2f 100644 --- a/src/builder/create_thread.rs +++ b/src/builder/create_thread.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -12,7 +14,7 @@ use crate::model::prelude::*; #[derive(Clone, Debug, Serialize)] #[must_use] pub struct CreateThread<'a> { - name: String, + name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] auto_archive_duration: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -29,7 +31,7 @@ pub struct CreateThread<'a> { impl<'a> CreateThread<'a> { /// Creates a builder with the given thread name, leaving all other fields empty. - pub fn new(name: impl Into) -> Self { + pub fn new(name: impl Into>) -> Self { Self { name: name.into(), auto_archive_duration: None, @@ -43,7 +45,7 @@ impl<'a> CreateThread<'a> { /// The name of the thread. Replaces the current value as set in [`Self::new`]. /// /// **Note**: Must be between 2 and 100 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } diff --git a/src/builder/create_webhook.rs b/src/builder/create_webhook.rs index 40efee3c009..5c798ffff7a 100644 --- a/src/builder/create_webhook.rs +++ b/src/builder/create_webhook.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -12,7 +14,7 @@ use crate::model::prelude::*; #[derive(Clone, Debug, Serialize)] #[must_use] pub struct CreateWebhook<'a> { - name: String, + name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] avatar: Option, @@ -22,7 +24,7 @@ pub struct CreateWebhook<'a> { impl<'a> CreateWebhook<'a> { /// Creates a new builder with the given webhook name, leaving all other fields empty. - pub fn new(name: impl Into) -> Self { + pub fn new(name: impl Into>) -> Self { Self { name: name.into(), avatar: None, @@ -33,13 +35,13 @@ impl<'a> CreateWebhook<'a> { /// Set the webhook's name, replacing the current value as set in [`Self::new`]. /// /// This must be between 1-80 characters. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = name.into(); self } /// Set the webhook's default avatar. - pub fn avatar(mut self, avatar: &CreateAttachment) -> Self { + pub fn avatar(mut self, avatar: &CreateAttachment<'_>) -> Self { self.avatar = Some(avatar.to_base64()); self } diff --git a/src/builder/edit_automod_rule.rs b/src/builder/edit_automod_rule.rs index 338e03d0c44..e7c5612f3c6 100644 --- a/src/builder/edit_automod_rule.rs +++ b/src/builder/edit_automod_rule.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -18,18 +20,18 @@ use crate::model::prelude::*; #[must_use] pub struct EditAutoModRule<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, event_type: EventType, #[serde(flatten, skip_serializing_if = "Option::is_none")] trigger: Option, #[serde(skip_serializing_if = "Option::is_none")] - actions: Option>, + actions: Option>, #[serde(skip_serializing_if = "Option::is_none")] enabled: Option, #[serde(skip_serializing_if = "Option::is_none")] - exempt_roles: Option>, + exempt_roles: Option>, #[serde(skip_serializing_if = "Option::is_none")] - exempt_channels: Option>, + exempt_channels: Option>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -42,7 +44,7 @@ impl<'a> EditAutoModRule<'a> { } /// The display name of the rule. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -62,8 +64,8 @@ impl<'a> EditAutoModRule<'a> { } /// Set the actions which will execute when the rule is triggered. - pub fn actions(mut self, actions: Vec) -> Self { - self.actions = Some(actions); + pub fn actions(mut self, actions: impl Into>) -> Self { + self.actions = Some(actions.into()); self } @@ -76,19 +78,16 @@ impl<'a> EditAutoModRule<'a> { /// Set roles that should not be affected by the rule. /// /// Maximum of 20. - pub fn exempt_roles(mut self, roles: impl IntoIterator>) -> Self { - self.exempt_roles = Some(roles.into_iter().map(Into::into).collect()); + pub fn exempt_roles(mut self, roles: impl Into>) -> Self { + self.exempt_roles = Some(roles.into()); self } /// Set channels that should not be affected by the rule. /// /// Maximum of 50. - pub fn exempt_channels( - mut self, - channels: impl IntoIterator>, - ) -> Self { - self.exempt_channels = Some(channels.into_iter().map(Into::into).collect()); + pub fn exempt_channels(mut self, channels: impl Into>) -> Self { + self.exempt_channels = Some(channels.into()); self } diff --git a/src/builder/edit_channel.rs b/src/builder/edit_channel.rs index d5b6572838c..1323b6d01c4 100644 --- a/src/builder/edit_channel.rs +++ b/src/builder/edit_channel.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateForumTag; @@ -34,14 +36,14 @@ use crate::model::prelude::*; #[must_use] pub struct EditChannel<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "type")] kind: Option, #[serde(skip_serializing_if = "Option::is_none")] position: Option, #[serde(skip_serializing_if = "Option::is_none")] - topic: Option, + topic: Option>, #[serde(skip_serializing_if = "Option::is_none")] nsfw: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -51,11 +53,11 @@ pub struct EditChannel<'a> { #[serde(skip_serializing_if = "Option::is_none")] user_limit: Option, #[serde(skip_serializing_if = "Option::is_none")] - permission_overwrites: Option>, + permission_overwrites: Option>, #[serde(skip_serializing_if = "Option::is_none")] parent_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] - rtc_region: Option>, + rtc_region: Option>>, #[serde(skip_serializing_if = "Option::is_none")] video_quality_mode: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -63,7 +65,7 @@ pub struct EditChannel<'a> { #[serde(skip_serializing_if = "Option::is_none")] flags: Option, #[serde(skip_serializing_if = "Option::is_none")] - available_tags: Option>, + available_tags: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] default_reaction_emoji: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -73,7 +75,7 @@ pub struct EditChannel<'a> { #[serde(skip_serializing_if = "Option::is_none")] default_forum_layout: Option, #[serde(skip_serializing_if = "Option::is_none")] - status: Option, + status: Option>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -110,7 +112,7 @@ impl<'a> EditChannel<'a> { /// This is for [voice] channels only. /// /// [voice]: ChannelType::Voice - pub fn voice_region(mut self, id: Option) -> Self { + pub fn voice_region(mut self, id: Option>) -> Self { self.rtc_region = Some(id); self } @@ -118,7 +120,7 @@ impl<'a> EditChannel<'a> { /// The name of the channel. /// /// Must be between 2 and 100 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -136,7 +138,7 @@ impl<'a> EditChannel<'a> { /// This is for [text] channels only. /// /// [text]: ChannelType::Text - pub fn topic(mut self, topic: impl Into) -> Self { + pub fn topic(mut self, topic: impl Into>) -> Self { self.topic = Some(topic.into()); self } @@ -219,16 +221,14 @@ impl<'a> EditChannel<'a> { /// # Ok(()) /// # } /// ``` - pub fn permissions(mut self, perms: impl IntoIterator) -> Self { - let overwrites = perms.into_iter().map(Into::into).collect::>(); - - self.permission_overwrites = Some(overwrites); + pub fn permissions(mut self, overwrites: impl Into>) -> Self { + self.permission_overwrites = Some(overwrites.into()); self } /// If this is a forum channel, sets the tags that can be assigned to forum posts. - pub fn available_tags(mut self, tags: impl IntoIterator) -> Self { - self.available_tags = Some(tags.into_iter().collect()); + pub fn available_tags(mut self, tags: impl Into]>>) -> Self { + self.available_tags = Some(tags.into()); self } diff --git a/src/builder/edit_guild.rs b/src/builder/edit_guild.rs index c02fbb86011..1de086e752d 100644 --- a/src/builder/edit_guild.rs +++ b/src/builder/edit_guild.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -14,7 +16,7 @@ use crate::model::prelude::*; #[must_use] pub struct EditGuild<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, // [Omitting region because Discord deprecated it] #[serde(skip_serializing_if = "Option::is_none")] verification_level: Option, @@ -31,11 +33,11 @@ pub struct EditGuild<'a> { #[serde(skip_serializing_if = "Option::is_none")] owner_id: Option, #[serde(skip_serializing_if = "Option::is_none")] - splash: Option>, + splash: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - discovery_splash: Option>, + discovery_splash: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - banner: Option>, + banner: Option>>, #[serde(skip_serializing_if = "Option::is_none")] system_channel_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -45,11 +47,11 @@ pub struct EditGuild<'a> { #[serde(skip_serializing_if = "Option::is_none")] public_updates_channel_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] - preferred_locale: Option>, + preferred_locale: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - features: Option>, + features: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, #[serde(skip_serializing_if = "Option::is_none")] premium_progress_bar_enabled: Option, @@ -100,7 +102,7 @@ impl<'a> EditGuild<'a> { /// # Ok(()) /// # } /// ``` - pub fn icon(mut self, icon: Option<&CreateAttachment>) -> Self { + pub fn icon(mut self, icon: Option<&CreateAttachment<'_>>) -> Self { self.icon = Some(icon.map(CreateAttachment::to_base64)); self } @@ -114,7 +116,7 @@ impl<'a> EditGuild<'a> { /// Set the name of the guild. /// /// **Note**: Must be between (and including) 2-100 characters. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -125,7 +127,7 @@ impl<'a> EditGuild<'a> { /// this through a guild's [`features`] list. /// /// [`features`]: Guild::features - pub fn description(mut self, name: impl Into) -> Self { + pub fn description(mut self, name: impl Into>) -> Self { self.description = Some(name.into()); self } @@ -136,8 +138,8 @@ impl<'a> EditGuild<'a> { /// this through a guild's [`features`] list. /// /// [`features`]: Guild::features - pub fn features(mut self, features: Vec) -> Self { - self.features = Some(features); + pub fn features(mut self, features: impl Into]>>) -> Self { + self.features = Some(features.into()); self } @@ -158,7 +160,7 @@ impl<'a> EditGuild<'a> { /// through a guild's [`features`] list. /// /// [`features`]: Guild::features - pub fn splash(mut self, splash: Option) -> Self { + pub fn splash(mut self, splash: Option>) -> Self { self.splash = Some(splash); self } @@ -171,7 +173,7 @@ impl<'a> EditGuild<'a> { /// a guild's [`features`] list. /// /// [`features`]: Guild::features - pub fn discovery_splash(mut self, splash: Option) -> Self { + pub fn discovery_splash(mut self, splash: Option>) -> Self { self.discovery_splash = Some(splash); self } @@ -184,7 +186,7 @@ impl<'a> EditGuild<'a> { /// guild's [`features`] list. /// /// [`features`]: Guild::features - pub fn banner(mut self, banner: Option) -> Self { + pub fn banner(mut self, banner: Option>) -> Self { self.banner = Some(banner); self } @@ -216,7 +218,7 @@ impl<'a> EditGuild<'a> { /// If this is not set, the locale will default to "en-US"; /// /// **Note**: This feature is for Community guilds only. - pub fn preferred_locale(mut self, locale: Option) -> Self { + pub fn preferred_locale(mut self, locale: Option>) -> Self { self.preferred_locale = Some(locale); self } diff --git a/src/builder/edit_guild_welcome_screen.rs b/src/builder/edit_guild_welcome_screen.rs index ccb481d485d..971956c4119 100644 --- a/src/builder/edit_guild_welcome_screen.rs +++ b/src/builder/edit_guild_welcome_screen.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -14,10 +16,10 @@ use crate::model::prelude::*; pub struct EditGuildWelcomeScreen<'a> { #[serde(skip_serializing_if = "Option::is_none")] enabled: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - welcome_channels: Vec, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + welcome_channels: Cow<'a, [CreateGuildWelcomeChannel<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -36,19 +38,22 @@ impl<'a> EditGuildWelcomeScreen<'a> { } /// The server description shown in the welcome screen. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } - pub fn add_welcome_channel(mut self, channel: CreateGuildWelcomeChannel) -> Self { - self.welcome_channels.push(channel); + pub fn add_welcome_channel(mut self, channel: CreateGuildWelcomeChannel<'a>) -> Self { + self.welcome_channels.to_mut().push(channel); self } /// Channels linked in the welcome screen and their display options - pub fn set_welcome_channels(mut self, channels: Vec) -> Self { - self.welcome_channels = channels; + pub fn set_welcome_channels( + mut self, + channels: impl Into]>>, + ) -> Self { + self.welcome_channels = channels.into(); self } @@ -88,32 +93,51 @@ impl<'a> Builder for EditGuildWelcomeScreen<'a> { /// [Discord docs](https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure) #[derive(Clone, Debug, Serialize)] #[must_use] -pub struct CreateGuildWelcomeChannel(GuildWelcomeChannel); +pub struct CreateGuildWelcomeChannel<'a> { + channel_id: ChannelId, + emoji_name: Option, + emoji_id: Option, + description: Cow<'a, str>, +} -impl CreateGuildWelcomeChannel { - pub fn new(channel_id: ChannelId, description: String) -> Self { - Self(GuildWelcomeChannel { +impl<'a> CreateGuildWelcomeChannel<'a> { + pub fn new(channel_id: ChannelId, description: impl Into>) -> Self { + Self { channel_id, - emoji: None, + emoji_id: None, + emoji_name: None, description: description.into(), - }) + } } /// The Id of the channel to show. pub fn id(mut self, id: impl Into) -> Self { - self.0.channel_id = id.into(); + self.channel_id = id.into(); self } /// The description shown for the channel. - pub fn description(mut self, description: impl Into) -> Self { - self.0.description = description.into().into(); + pub fn description(mut self, description: impl Into>) -> Self { + self.description = description.into(); self } /// The emoji shown for the channel. pub fn emoji(mut self, emoji: GuildWelcomeChannelEmoji) -> Self { - self.0.emoji = Some(emoji); + match emoji { + GuildWelcomeChannelEmoji::Custom { + id, + name, + } => { + self.emoji_id = Some(id); + self.emoji_name = Some(name.into()); + }, + GuildWelcomeChannelEmoji::Unicode(name) => { + self.emoji_id = None; + self.emoji_name = Some(name.into()); + }, + }; + self } } diff --git a/src/builder/edit_interaction_response.rs b/src/builder/edit_interaction_response.rs index f6bd18b843f..17228f62f71 100644 --- a/src/builder/edit_interaction_response.rs +++ b/src/builder/edit_interaction_response.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::{ @@ -17,9 +19,9 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct EditInteractionResponse(EditWebhookMessage); +pub struct EditInteractionResponse<'a>(EditWebhookMessage<'a>); -impl EditInteractionResponse { +impl<'a> EditInteractionResponse<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -29,21 +31,21 @@ impl EditInteractionResponse { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(self, content: impl Into) -> Self { + pub fn content(self, content: impl Into>) -> Self { Self(self.0.content(content)) } /// Adds an embed for the message. /// /// Embeds from the original message are reset when adding new embeds and must be re-added. - pub fn add_embed(self, embed: CreateEmbed) -> Self { + pub fn add_embed(self, embed: CreateEmbed<'a>) -> Self { Self(self.0.add_embed(embed)) } /// Adds multiple embeds to the message. /// /// Embeds from the original message are reset when adding new embeds and must be re-added. - pub fn add_embeds(self, embeds: Vec) -> Self { + pub fn add_embeds(self, embeds: impl IntoIterator>) -> Self { Self(self.0.add_embeds(embeds)) } @@ -51,7 +53,7 @@ impl EditInteractionResponse { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embed`] /// instead. - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { Self(self.0.embed(embed)) } @@ -61,30 +63,30 @@ impl EditInteractionResponse { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embeds`] /// instead. - pub fn embeds(self, embeds: Vec) -> Self { + pub fn embeds(self, embeds: impl Into]>>) -> Self { Self(self.0.embeds(embeds)) } /// Set the allowed mentions for the message. - pub fn allowed_mentions(self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { Self(self.0.allowed_mentions(allowed_mentions)) } /// Sets the components of this message. - pub fn components(self, components: Vec) -> Self { + pub fn components(self, components: impl Into]>>) -> Self { Self(self.0.components(components)) } super::button_and_select_menu_convenience_methods!(self.0.components); /// Sets attachments, see [`EditAttachments`] for more details. - pub fn attachments(self, attachments: EditAttachments) -> Self { + pub fn attachments(self, attachments: EditAttachments<'a>) -> Self { Self(self.0.attachments(attachments)) } /// Adds a new attachment to the message. /// /// Resets existing attachments. See the documentation for [`EditAttachments`] for details. - pub fn new_attachment(self, attachment: CreateAttachment) -> Self { + pub fn new_attachment(self, attachment: CreateAttachment<'a>) -> Self { Self(self.0.new_attachment(attachment)) } @@ -101,7 +103,7 @@ impl EditInteractionResponse { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for EditInteractionResponse { +impl Builder for EditInteractionResponse<'_> { type Context<'ctx> = &'ctx str; type Built = Message; diff --git a/src/builder/edit_member.rs b/src/builder/edit_member.rs index 1cf965e1d29..ca24608b268 100644 --- a/src/builder/edit_member.rs +++ b/src/builder/edit_member.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -14,9 +16,9 @@ use crate::model::prelude::*; #[must_use] pub struct EditMember<'a> { #[serde(skip_serializing_if = "Option::is_none")] - nick: Option, + nick: Option>, #[serde(skip_serializing_if = "Option::is_none")] - roles: Option>, + roles: Option>, #[serde(skip_serializing_if = "Option::is_none")] mute: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -24,7 +26,7 @@ pub struct EditMember<'a> { #[serde(skip_serializing_if = "Option::is_none")] channel_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] - communication_disabled_until: Option>, + communication_disabled_until: Option>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, @@ -63,7 +65,7 @@ impl<'a> EditMember<'a> { /// **Note**: Requires the [Manage Nicknames] permission. /// /// [Manage Nicknames]: Permissions::MANAGE_NICKNAMES - pub fn nickname(mut self, nickname: impl Into) -> Self { + pub fn nickname(mut self, nickname: impl Into>) -> Self { self.nick = Some(nickname.into()); self } @@ -73,8 +75,8 @@ impl<'a> EditMember<'a> { /// **Note**: Requires the [Manage Roles] permission to modify. /// /// [Manage Roles]: Permissions::MANAGE_ROLES - pub fn roles(mut self, roles: impl IntoIterator>) -> Self { - self.roles = Some(roles.into_iter().map(Into::into).collect()); + pub fn roles(mut self, roles: impl Into>) -> Self { + self.roles = Some(roles.into()); self } @@ -101,30 +103,18 @@ impl<'a> EditMember<'a> { /// Times the user out until `time`, an ISO8601-formatted datetime string. /// - /// `time` is considered invalid if it is not a valid ISO8601 timestamp or if it is greater + /// `time` is considered invalid if it is greater /// than 28 days from the current time. /// /// **Note**: Requires the [Moderate Members] permission. /// /// [Moderate Members]: Permissions::MODERATE_MEMBERS #[doc(alias = "timeout")] - pub fn disable_communication_until(mut self, time: String) -> Self { + pub fn disable_communication_until(mut self, time: Timestamp) -> Self { self.communication_disabled_until = Some(Some(time)); self } - /// Times the user out until `time`. - /// - /// `time` is considered invalid if it is greater than 28 days from the current time. - /// - /// **Note**: Requires the [Moderate Members] permission. - /// - /// [Moderate Members]: Permissions::MODERATE_MEMBERS - #[doc(alias = "timeout")] - pub fn disable_communication_until_datetime(self, time: Timestamp) -> Self { - self.disable_communication_until(time.to_string()) - } - /// Allow a user to communicate, removing their timeout, if there is one. /// /// **Note**: Requires the [Moderate Members] permission. diff --git a/src/builder/edit_message.rs b/src/builder/edit_message.rs index 93b181cb8ff..123270c8645 100644 --- a/src/builder/edit_message.rs +++ b/src/builder/edit_message.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::{check_overflow, Builder}; use super::{ @@ -37,22 +39,22 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/channel#edit-message) #[derive(Clone, Debug, Default, Serialize, PartialEq)] #[must_use] -pub struct EditMessage { +pub struct EditMessage<'a> { #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, #[serde(skip_serializing_if = "Option::is_none")] - embeds: Option>, + embeds: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] - components: Option>, + components: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] - attachments: Option, + attachments: Option>, } -impl EditMessage { +impl<'a> EditMessage<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -68,7 +70,7 @@ impl EditMessage { if let Some(embeds) = &self.embeds { check_overflow(embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in embeds { + for embed in embeds.iter() { embed.check_length()?; } } @@ -80,7 +82,7 @@ impl EditMessage { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -89,8 +91,8 @@ impl EditMessage { /// /// **Note**: This will keep all existing embeds. Use [`Self::embed()`] to replace existing /// embeds. - pub fn add_embed(mut self, embed: CreateEmbed) -> Self { - self.embeds.get_or_insert_with(Vec::new).push(embed); + pub fn add_embed(mut self, embed: CreateEmbed<'a>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().push(embed); self } @@ -98,8 +100,8 @@ impl EditMessage { /// /// **Note**: This will keep all existing embeds. Use [`Self::embeds()`] to replace existing /// embeds. - pub fn add_embeds(mut self, embeds: Vec) -> Self { - self.embeds.get_or_insert_with(Vec::new).extend(embeds); + pub fn add_embeds(mut self, embeds: impl IntoIterator>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().extend(embeds); self } @@ -107,7 +109,7 @@ impl EditMessage { /// /// **Note**: This will replace all existing embeds. Use [`Self::add_embed()`] to keep existing /// embeds. - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { self.embeds(vec![embed]) } @@ -115,8 +117,8 @@ impl EditMessage { /// /// **Note**: This will replace all existing embeds. Use [`Self::add_embeds()`] to keep existing /// embeds. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = Some(embeds); + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = Some(embeds.into()); self } @@ -163,14 +165,14 @@ impl EditMessage { } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } /// Sets the components of this message. - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); @@ -182,7 +184,7 @@ impl EditMessage { } /// Sets attachments, see [`EditAttachments`] for more details. - pub fn attachments(mut self, attachments: EditAttachments) -> Self { + pub fn attachments(mut self, attachments: EditAttachments<'a>) -> Self { self.attachments = Some(attachments); self } @@ -190,7 +192,7 @@ impl EditMessage { /// Adds a new attachment to the message. /// /// Resets existing attachments. See the documentation for [`EditAttachments`] for details. - pub fn new_attachment(mut self, attachment: CreateAttachment) -> Self { + pub fn new_attachment(mut self, attachment: CreateAttachment<'a>) -> Self { let attachments = self.attachments.get_or_insert_with(Default::default); self.attachments = Some(std::mem::take(attachments).add(attachment)); self @@ -220,7 +222,7 @@ impl EditMessage { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for EditMessage { +impl Builder for EditMessage<'_> { type Context<'ctx> = (ChannelId, MessageId, Option); type Built = Message; diff --git a/src/builder/edit_profile.rs b/src/builder/edit_profile.rs index 9656181ec9c..1fd04be0a4e 100644 --- a/src/builder/edit_profile.rs +++ b/src/builder/edit_profile.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -14,16 +16,16 @@ use crate::model::user::CurrentUser; /// [Discord docs](https://discord.com/developers/docs/resources/user#modify-current-user) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct EditProfile { +pub struct EditProfile<'a> { #[serde(skip_serializing_if = "Option::is_none")] - username: Option, + username: Option>, #[serde(skip_serializing_if = "Option::is_none")] avatar: Option>, #[serde(skip_serializing_if = "Option::is_none")] banner: Option>, } -impl EditProfile { +impl<'a> EditProfile<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -46,7 +48,7 @@ impl EditProfile { /// # Ok(()) /// # } /// ``` - pub fn avatar(mut self, avatar: &CreateAttachment) -> Self { + pub fn avatar(mut self, avatar: &CreateAttachment<'_>) -> Self { self.avatar = Some(Some(avatar.to_base64())); self } @@ -62,13 +64,13 @@ impl EditProfile { /// When modifying the username, if another user has the same _new_ username and current /// discriminator, a new unique discriminator will be assigned. If there are no available /// discriminators with the requested username, an error will occur. - pub fn username(mut self, username: impl Into) -> Self { + pub fn username(mut self, username: impl Into>) -> Self { self.username = Some(username.into()); self } /// Sets the banner of the current user. - pub fn banner(mut self, banner: &CreateAttachment) -> Self { + pub fn banner(mut self, banner: &CreateAttachment<'_>) -> Self { self.banner = Some(Some(banner.to_base64())); self } @@ -82,7 +84,7 @@ impl EditProfile { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for EditProfile { +impl Builder for EditProfile<'_> { type Context<'ctx> = (); type Built = CurrentUser; diff --git a/src/builder/edit_role.rs b/src/builder/edit_role.rs index 55f9d44bdf6..4072422c783 100644 --- a/src/builder/edit_role.rs +++ b/src/builder/edit_role.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; use super::CreateAttachment; @@ -46,7 +48,7 @@ use crate::model::prelude::*; #[must_use] pub struct EditRole<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, #[serde(skip_serializing_if = "Option::is_none")] permissions: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -55,9 +57,9 @@ pub struct EditRole<'a> { #[serde(skip_serializing_if = "Option::is_none")] hoist: Option, #[serde(skip_serializing_if = "Option::is_none")] - icon: Option>, + icon: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - unicode_emoji: Option>, + unicode_emoji: Option>>, #[serde(skip_serializing_if = "Option::is_none")] mentionable: Option, @@ -75,15 +77,15 @@ impl<'a> EditRole<'a> { } /// Creates a new builder with the values of the given [`Role`]. - pub fn from_role(role: &Role) -> Self { + pub fn from_role(role: &'a Role) -> Self { EditRole { hoist: Some(role.hoist()), mentionable: Some(role.mentionable()), - name: Some(role.name.clone()), + name: Some(Cow::Borrowed(&role.name)), permissions: Some(role.permissions.bits()), position: Some(role.position), colour: Some(role.colour), - unicode_emoji: role.unicode_emoji.as_ref().map(|v| Some(v.clone())), + unicode_emoji: role.unicode_emoji.as_ref().map(|v| Some(Cow::Borrowed(v.as_str()))), audit_log_reason: None, // TODO: Do we want to download role.icon? icon: None, @@ -110,8 +112,8 @@ impl<'a> EditRole<'a> { } /// Set the role's name. - pub fn name(mut self, name: impl Into) -> Self { - self.name = Some(name.into().into()); + pub fn name(mut self, name: impl Into>) -> Self { + self.name = Some(name.into()); self } @@ -136,7 +138,7 @@ impl<'a> EditRole<'a> { } /// Set the role icon to a custom image. - pub fn icon(mut self, icon: Option<&CreateAttachment>) -> Self { + pub fn icon(mut self, icon: Option<&CreateAttachment<'_>>) -> Self { self.icon = Some(icon.map(CreateAttachment::to_base64).map(Into::into)); self.unicode_emoji = Some(None); self diff --git a/src/builder/edit_scheduled_event.rs b/src/builder/edit_scheduled_event.rs index 7e19132917f..44afc3bf115 100644 --- a/src/builder/edit_scheduled_event.rs +++ b/src/builder/edit_scheduled_event.rs @@ -1,6 +1,8 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; -use super::CreateAttachment; +use super::{CreateAttachment, CreateScheduledEventMetadata}; #[cfg(feature = "http")] use crate::http::CacheHttp; #[cfg(feature = "http")] @@ -14,17 +16,17 @@ pub struct EditScheduledEvent<'a> { #[serde(skip_serializing_if = "Option::is_none")] channel_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] - entity_metadata: Option>, + entity_metadata: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, #[serde(skip_serializing_if = "Option::is_none")] privacy_level: Option, #[serde(skip_serializing_if = "Option::is_none")] - scheduled_start_time: Option, + scheduled_start_time: Option, #[serde(skip_serializing_if = "Option::is_none")] - scheduled_end_time: Option, + scheduled_end_time: Option, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, #[serde(skip_serializing_if = "Option::is_none")] entity_type: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -54,7 +56,7 @@ impl<'a> EditScheduledEvent<'a> { } /// Sets the name of the scheduled event. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -66,7 +68,7 @@ impl<'a> EditScheduledEvent<'a> { } /// Sets the description of the scheduled event. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -74,7 +76,7 @@ impl<'a> EditScheduledEvent<'a> { /// Sets the start time of the scheduled event. #[inline] pub fn start_time(mut self, timestamp: impl Into) -> Self { - self.scheduled_start_time = Some(timestamp.into().to_string()); + self.scheduled_start_time = Some(timestamp.into()); self } @@ -86,7 +88,7 @@ impl<'a> EditScheduledEvent<'a> { /// [`External`]: ScheduledEventType::External #[inline] pub fn end_time(mut self, timestamp: impl Into) -> Self { - self.scheduled_end_time = Some(timestamp.into().to_string()); + self.scheduled_end_time = Some(timestamp.into()); self } @@ -146,15 +148,15 @@ impl<'a> EditScheduledEvent<'a> { /// /// [`kind`]: EditScheduledEvent::kind /// [`External`]: ScheduledEventType::External - pub fn location(mut self, location: impl Into) -> Self { - self.entity_metadata = Some(Some(ScheduledEventMetadata { - location: Some(location.into().into()), + pub fn location(mut self, location: impl Into>) -> Self { + self.entity_metadata = Some(Some(CreateScheduledEventMetadata { + location: Some(location.into()), })); self } /// Sets the cover image for the scheduled event. - pub fn image(mut self, image: &CreateAttachment) -> Self { + pub fn image(mut self, image: &CreateAttachment<'_>) -> Self { self.image = Some(image.to_base64()); self } diff --git a/src/builder/edit_stage_instance.rs b/src/builder/edit_stage_instance.rs index d69812e4cf8..b366337b9b5 100644 --- a/src/builder/edit_stage_instance.rs +++ b/src/builder/edit_stage_instance.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -13,7 +15,7 @@ use crate::model::prelude::*; #[must_use] pub struct EditStageInstance<'a> { #[serde(skip_serializing_if = "Option::is_none")] - topic: Option, + topic: Option>, #[serde(skip_serializing_if = "Option::is_none")] privacy_level: Option, @@ -28,7 +30,7 @@ impl<'a> EditStageInstance<'a> { } /// Sets the topic of the stage channel instance. - pub fn topic(mut self, topic: impl Into) -> Self { + pub fn topic(mut self, topic: impl Into>) -> Self { self.topic = Some(topic.into()); self } diff --git a/src/builder/edit_sticker.rs b/src/builder/edit_sticker.rs index 2b8af762646..dd1b51eb742 100644 --- a/src/builder/edit_sticker.rs +++ b/src/builder/edit_sticker.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -21,11 +23,11 @@ use crate::model::prelude::*; #[must_use] pub struct EditSticker<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + description: Option>, #[serde(skip_serializing_if = "Option::is_none")] - tags: Option, + tags: Option>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -40,7 +42,7 @@ impl<'a> EditSticker<'a> { /// The name of the sticker to set. /// /// **Note**: Must be between 2 and 30 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -48,7 +50,7 @@ impl<'a> EditSticker<'a> { /// The description of the sticker. /// /// **Note**: If not empty, must be between 2 and 100 characters long. - pub fn description(mut self, description: impl Into) -> Self { + pub fn description(mut self, description: impl Into>) -> Self { self.description = Some(description.into()); self } @@ -56,7 +58,7 @@ impl<'a> EditSticker<'a> { /// The Discord name of a unicode emoji representing the sticker's expression. /// /// **Note**: Must be between 2 and 200 characters long. - pub fn tags(mut self, tags: impl Into) -> Self { + pub fn tags(mut self, tags: impl Into>) -> Self { self.tags = Some(tags.into()); self } diff --git a/src/builder/edit_thread.rs b/src/builder/edit_thread.rs index ca3a5214148..7d444fb425c 100644 --- a/src/builder/edit_thread.rs +++ b/src/builder/edit_thread.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::Builder; #[cfg(feature = "http")] @@ -11,7 +13,7 @@ use crate::model::prelude::*; #[must_use] pub struct EditThread<'a> { #[serde(skip_serializing_if = "Option::is_none")] - name: Option, + name: Option>, #[serde(skip_serializing_if = "Option::is_none")] archived: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -25,7 +27,7 @@ pub struct EditThread<'a> { #[serde(skip_serializing_if = "Option::is_none")] flags: Option, #[serde(skip_serializing_if = "Option::is_none")] - applied_tags: Option>, + applied_tags: Option>, #[serde(skip)] audit_log_reason: Option<&'a str>, @@ -40,7 +42,7 @@ impl<'a> EditThread<'a> { /// The name of the thread. /// /// **Note**: Must be between 2 and 100 characters long. - pub fn name(mut self, name: impl Into) -> Self { + pub fn name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } @@ -90,8 +92,8 @@ impl<'a> EditThread<'a> { } /// If this is a forum post, edits the assigned tags of this forum post. - pub fn applied_tags(mut self, applied_tags: impl IntoIterator) -> Self { - self.applied_tags = Some(applied_tags.into_iter().collect()); + pub fn applied_tags(mut self, applied_tags: impl Into>) -> Self { + self.applied_tags = Some(applied_tags.into()); self } diff --git a/src/builder/edit_webhook.rs b/src/builder/edit_webhook.rs index 9b2bc429d84..2d3825ba183 100644 --- a/src/builder/edit_webhook.rs +++ b/src/builder/edit_webhook.rs @@ -43,7 +43,7 @@ impl<'a> EditWebhook<'a> { } /// Set the webhook's default avatar. - pub fn avatar(mut self, avatar: &CreateAttachment) -> Self { + pub fn avatar(mut self, avatar: &CreateAttachment<'_>) -> Self { self.avatar = Some(Some(avatar.to_base64())); self } diff --git a/src/builder/edit_webhook_message.rs b/src/builder/edit_webhook_message.rs index 6053dac9248..6a76d73003c 100644 --- a/src/builder/edit_webhook_message.rs +++ b/src/builder/edit_webhook_message.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::{check_overflow, Builder}; use super::{ @@ -20,23 +22,23 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/webhook#edit-webhook-message) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct EditWebhookMessage { +pub struct EditWebhookMessage<'a> { #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, #[serde(skip_serializing_if = "Option::is_none")] - embeds: Option>, + embeds: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) components: Option>, + pub(crate) components: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) attachments: Option, + pub(crate) attachments: Option>, #[serde(skip)] thread_id: Option, } -impl EditWebhookMessage { +impl<'a> EditWebhookMessage<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -52,7 +54,7 @@ impl EditWebhookMessage { if let Some(embeds) = &self.embeds { check_overflow(embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in embeds { + for embed in embeds.iter() { embed.check_length()?; } } @@ -64,7 +66,7 @@ impl EditWebhookMessage { /// /// **Note**: Message contents must be under 2000 unicode code points. #[inline] - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -80,16 +82,16 @@ impl EditWebhookMessage { /// Adds an embed for the message. /// /// Embeds from the original message are reset when adding new embeds and must be re-added. - pub fn add_embed(mut self, embed: CreateEmbed) -> Self { - self.embeds.get_or_insert(Vec::new()).push(embed); + pub fn add_embed(mut self, embed: CreateEmbed<'a>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().push(embed); self } /// Adds multiple embeds to the message. /// /// Embeds from the original message are reset when adding new embeds and must be re-added. - pub fn add_embeds(mut self, embeds: Vec) -> Self { - self.embeds.get_or_insert(Vec::new()).extend(embeds); + pub fn add_embeds(mut self, embeds: impl IntoIterator>) -> Self { + self.embeds.get_or_insert_with(Cow::default).to_mut().extend(embeds); self } @@ -97,9 +99,8 @@ impl EditWebhookMessage { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embed`] /// instead. - pub fn embed(mut self, embed: CreateEmbed) -> Self { - self.embeds = Some(vec![embed]); - self + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { + self.embeds(vec![embed]) } /// Sets the embeds for the message. @@ -108,13 +109,13 @@ impl EditWebhookMessage { /// /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embeds`] /// instead. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = Some(embeds); + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = Some(embeds.into()); self } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } @@ -125,14 +126,14 @@ impl EditWebhookMessage { /// /// [`WebhookType::Application`]: crate::model::webhook::WebhookType /// [`WebhookType::Incoming`]: crate::model::webhook::WebhookType - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); /// Sets attachments, see [`EditAttachments`] for more details. - pub fn attachments(mut self, attachments: EditAttachments) -> Self { + pub fn attachments(mut self, attachments: EditAttachments<'a>) -> Self { self.attachments = Some(attachments); self } @@ -140,7 +141,7 @@ impl EditWebhookMessage { /// Adds a new attachment to the message. /// /// Resets existing attachments. See the documentation for [`EditAttachments`] for details. - pub fn new_attachment(mut self, attachment: CreateAttachment) -> Self { + pub fn new_attachment(mut self, attachment: CreateAttachment<'a>) -> Self { let attachments = self.attachments.get_or_insert_with(Default::default); self.attachments = Some(std::mem::take(attachments).add(attachment)); self @@ -162,7 +163,7 @@ impl EditWebhookMessage { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for EditWebhookMessage { +impl Builder for EditWebhookMessage<'_> { type Context<'ctx> = (WebhookId, &'ctx str, MessageId); type Built = Message; diff --git a/src/builder/execute_webhook.rs b/src/builder/execute_webhook.rs index 4265e9c38b8..ba50e648281 100644 --- a/src/builder/execute_webhook.rs +++ b/src/builder/execute_webhook.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[cfg(feature = "http")] use super::{check_overflow, Builder}; use super::{ @@ -58,30 +60,30 @@ use crate::model::prelude::*; /// [Discord docs](https://discord.com/developers/docs/resources/webhook#execute-webhook) #[derive(Clone, Debug, Default, Serialize)] #[must_use] -pub struct ExecuteWebhook { +pub struct ExecuteWebhook<'a> { #[serde(skip_serializing_if = "Option::is_none")] - content: Option, + content: Option>, #[serde(skip_serializing_if = "Option::is_none")] - username: Option, + username: Option>, #[serde(skip_serializing_if = "Option::is_none")] - avatar_url: Option, + avatar_url: Option>, tts: bool, - embeds: Vec, + embeds: Cow<'a, [CreateEmbed<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] - allowed_mentions: Option, + allowed_mentions: Option>, #[serde(skip_serializing_if = "Option::is_none")] - components: Option>, + components: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] flags: Option, #[serde(skip_serializing_if = "Option::is_none")] - thread_name: Option, - attachments: EditAttachments, + thread_name: Option>, + attachments: EditAttachments<'a>, #[serde(skip)] thread_id: Option, } -impl ExecuteWebhook { +impl<'a> ExecuteWebhook<'a> { /// Equivalent to [`Self::default`]. pub fn new() -> Self { Self::default() @@ -96,7 +98,7 @@ impl ExecuteWebhook { check_overflow(self.embeds.len(), constants::EMBED_MAX_COUNT) .map_err(|_| Error::Model(ModelError::EmbedAmount))?; - for embed in &self.embeds { + for embed in self.embeds.iter() { embed.check_length()?; } @@ -124,7 +126,7 @@ impl ExecuteWebhook { /// # Ok(()) /// # } /// ``` - pub fn avatar_url(mut self, avatar_url: impl Into) -> Self { + pub fn avatar_url(mut self, avatar_url: impl Into>) -> Self { self.avatar_url = Some(avatar_url.into()); self } @@ -155,7 +157,7 @@ impl ExecuteWebhook { /// # Ok(()) /// # } /// ``` - pub fn content(mut self, content: impl Into) -> Self { + pub fn content(mut self, content: impl Into>) -> Self { self.content = Some(content.into()); self } @@ -190,13 +192,13 @@ impl ExecuteWebhook { } /// Appends a file to the webhook message. - pub fn add_file(mut self, file: CreateAttachment) -> Self { + pub fn add_file(mut self, file: CreateAttachment<'a>) -> Self { self.attachments = self.attachments.add(file); self } /// Appends a list of files to the webhook message. - pub fn add_files(mut self, files: impl IntoIterator) -> Self { + pub fn add_files(mut self, files: impl IntoIterator>) -> Self { for file in files { self.attachments = self.attachments.add(file); } @@ -207,13 +209,13 @@ impl ExecuteWebhook { /// /// Calling this multiple times will overwrite the file list. To append files, call /// [`Self::add_file`] or [`Self::add_files`] instead. - pub fn files(mut self, files: impl IntoIterator) -> Self { + pub fn files(mut self, files: impl IntoIterator>) -> Self { self.attachments = EditAttachments::new(); self.add_files(files) } /// Set the allowed mentions for the message. - pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions<'a>) -> Self { self.allowed_mentions = Some(allowed_mentions); self } @@ -224,8 +226,8 @@ impl ExecuteWebhook { /// /// [`WebhookType::Application`]: crate::model::webhook::WebhookType /// [`WebhookType::Incoming`]: crate::model::webhook::WebhookType - pub fn components(mut self, components: Vec) -> Self { - self.components = Some(components); + pub fn components(mut self, components: impl Into]>>) -> Self { + self.components = Some(components.into()); self } super::button_and_select_menu_convenience_methods!(self.components); @@ -235,13 +237,13 @@ impl ExecuteWebhook { /// Refer to the [struct-level documentation] for an example on how to use embeds. /// /// [struct-level documentation]: #examples - pub fn embed(self, embed: CreateEmbed) -> Self { + pub fn embed(self, embed: CreateEmbed<'a>) -> Self { self.embeds(vec![embed]) } /// Set multiple embeds for the message. - pub fn embeds(mut self, embeds: Vec) -> Self { - self.embeds = embeds; + pub fn embeds(mut self, embeds: impl Into]>>) -> Self { + self.embeds = embeds.into(); self } @@ -296,7 +298,7 @@ impl ExecuteWebhook { /// # Ok(()) /// # } /// ``` - pub fn username(mut self, username: impl Into) -> Self { + pub fn username(mut self, username: impl Into>) -> Self { self.username = Some(username.into()); self } @@ -333,7 +335,7 @@ impl ExecuteWebhook { } /// Name of thread to create (requires the webhook channel to be a forum channel) - pub fn thread_name(mut self, thread_name: String) -> Self { + pub fn thread_name(mut self, thread_name: Cow<'a, str>) -> Self { self.thread_name = Some(thread_name); self } @@ -341,7 +343,7 @@ impl ExecuteWebhook { #[cfg(feature = "http")] #[async_trait::async_trait] -impl Builder for ExecuteWebhook { +impl Builder for ExecuteWebhook<'_> { type Context<'ctx> = (WebhookId, &'ctx str, bool); type Built = Option; diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 52b89e43fde..62675c95c4d 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -124,8 +124,8 @@ macro_rules! button_and_select_menu_convenience_methods { /// /// Convenience method that wraps [`Self::components`]. Arranges buttons in action rows /// automatically. - pub fn button(mut $self, button: super::CreateButton) -> Self { - let rows = $self$(.$components_path)+.get_or_insert_with(Vec::new); + pub fn button(mut $self, button: super::CreateButton<'a>) -> Self { + let rows = $self$(.$components_path)+.get_or_insert_with(Cow::default).to_mut(); let row_with_space_left = rows.last_mut().and_then(|row| match row { super::CreateActionRow::Buttons(buttons) if buttons.len() < 5 => Some(buttons), _ => None, @@ -140,9 +140,10 @@ macro_rules! button_and_select_menu_convenience_methods { /// Adds an interactive select menu to this message. /// /// Convenience method that wraps [`Self::components`]. - pub fn select_menu(mut $self, select_menu: super::CreateSelectMenu) -> Self { + pub fn select_menu(mut $self, select_menu: super::CreateSelectMenu<'a>) -> Self { $self$(.$components_path)+ - .get_or_insert_with(Vec::new) + .get_or_insert_with(Cow::default) + .to_mut() .push(super::CreateActionRow::SelectMenu(select_menu)); $self } diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index c8120d775f7..7c7a50084df 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -1004,7 +1004,7 @@ async fn send_grouped_commands_embed( flatten_group_to_string(&mut embed_text, group, 0, help_options)?; - embed = embed.field(group.name, &embed_text, true); + embed = embed.field(group.name, embed_text, true); } let builder = CreateMessage::new().embed(embed); diff --git a/src/http/client.rs b/src/http/client.rs index f41279192d6..2cc40b151cd 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -56,7 +56,7 @@ pub struct HttpBuilder { token: SecretString, proxy: Option, application_id: Option, - default_allowed_mentions: Option, + default_allowed_mentions: Option>, } impl HttpBuilder { @@ -133,7 +133,10 @@ impl HttpBuilder { /// /// This only takes effect if you are calling through the model or builder methods, not directly /// calling [`Http`] methods, as [`Http`] is simply used as a convenient storage for these. - pub fn default_allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self { + pub fn default_allowed_mentions( + mut self, + allowed_mentions: CreateAllowedMentions<'static>, + ) -> Self { self.default_allowed_mentions = Some(allowed_mentions); self } @@ -199,7 +202,7 @@ pub struct Http { pub proxy: Option, token: SecretString, application_id: AtomicU64, - pub default_allowed_mentions: Option, + pub default_allowed_mentions: Option>, } impl Http { @@ -470,7 +473,7 @@ impl Http { &self, channel_id: ChannelId, map: &impl serde::Serialize, - files: Vec, + files: Vec>, audit_log_reason: Option<&str>, ) -> Result { self.fire(Request { @@ -523,7 +526,7 @@ impl Http { &self, interaction_token: &str, map: &impl serde::Serialize, - files: Vec, + files: Vec>, ) -> Result { let mut request = Request { body: None, @@ -723,7 +726,7 @@ impl Http { interaction_id: InteractionId, interaction_token: &str, map: &impl serde::Serialize, - files: Vec, + files: Vec>, ) -> Result<()> { let mut request = Request { body: None, @@ -904,15 +907,15 @@ impl Http { pub async fn create_sticker( &self, guild_id: GuildId, - map: impl IntoIterator, - file: CreateAttachment, + map: impl IntoIterator)>, + file: CreateAttachment<'_>, audit_log_reason: Option<&str>, ) -> Result { self.fire(Request { body: None, multipart: Some(Multipart { upload: MultipartUpload::File(file), - fields: map.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + fields: map.into_iter().map(|(k, v)| (k.into(), v)).collect(), payload_json: None, }), headers: audit_log_reason.map(reason_into_header), @@ -1604,7 +1607,7 @@ impl Http { interaction_token: &str, message_id: MessageId, map: &impl serde::Serialize, - new_attachments: Vec, + new_attachments: Vec>, ) -> Result { let mut request = Request { body: None, @@ -1893,7 +1896,7 @@ impl Http { channel_id: ChannelId, message_id: MessageId, map: &impl serde::Serialize, - new_attachments: Vec, + new_attachments: Vec>, ) -> Result { let mut request = Request { body: None, @@ -2039,7 +2042,7 @@ impl Http { &self, interaction_token: &str, map: &impl serde::Serialize, - new_attachments: Vec, + new_attachments: Vec>, ) -> Result { let mut request = Request { body: None, @@ -2484,7 +2487,7 @@ impl Http { thread_id: Option, token: &str, wait: bool, - files: Vec, + files: Vec>, map: &impl serde::Serialize, ) -> Result> { let mut params = ArrayVec::<_, 2>::new(); @@ -2561,7 +2564,7 @@ impl Http { token: &str, message_id: MessageId, map: &impl serde::Serialize, - new_attachments: Vec, + new_attachments: Vec>, ) -> Result { let mut params = ArrayVec::<_, 1>::new(); if let Some(thread_id) = thread_id { @@ -4462,7 +4465,7 @@ impl Http { pub async fn send_message( &self, channel_id: ChannelId, - files: Vec, + files: Vec>, map: &impl serde::Serialize, ) -> Result { let mut request = Request { diff --git a/src/http/multipart.rs b/src/http/multipart.rs index 35e025af8d8..609a37694c9 100644 --- a/src/http/multipart.rs +++ b/src/http/multipart.rs @@ -5,7 +5,7 @@ use reqwest::multipart::{Form, Part}; use crate::builder::CreateAttachment; use crate::internal::prelude::*; -impl CreateAttachment { +impl<'a> CreateAttachment<'a> { fn into_part(self) -> Result { let mut part = Part::bytes(self.data); part = guess_mime_str(part, &self.filename)?; @@ -15,18 +15,18 @@ impl CreateAttachment { } #[derive(Clone, Debug)] -pub enum MultipartUpload { +pub enum MultipartUpload<'a> { /// A file sent with the form data as an individual upload. For example, a sticker. - File(CreateAttachment), + File(CreateAttachment<'a>), /// Files sent with the form as message attachments. - Attachments(Vec), + Attachments(Vec>), } /// Holder for multipart body. Contains upload data, multipart fields, and payload_json for /// creating requests with attachments. #[derive(Clone, Debug)] -pub struct Multipart { - pub upload: MultipartUpload, +pub struct Multipart<'a> { + pub upload: MultipartUpload<'a>, /// Multipart text fields that are sent with the form data as individual fields. If a certain /// endpoint does not support passing JSON body via `payload_json`, this must be used instead. pub fields: Vec<(Cow<'static, str>, Cow<'static, str>)>, @@ -34,7 +34,7 @@ pub struct Multipart { pub payload_json: Option, } -impl Multipart { +impl<'a> Multipart<'a> { pub(crate) fn build_form(self) -> Result
{ let mut multipart = Form::new(); diff --git a/src/http/request.rs b/src/http/request.rs index a051ef22e11..4c195a32bdd 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -22,7 +22,7 @@ use crate::internal::prelude::*; #[must_use] pub struct Request<'a, const MAX_PARAMS: usize> { pub(super) body: Option>, - pub(super) multipart: Option, + pub(super) multipart: Option>, pub(super) headers: Option, pub(super) method: LightMethod, pub(super) route: Route<'a>, @@ -50,7 +50,7 @@ impl<'a, const MAX_PARAMS: usize> Request<'a, MAX_PARAMS> { self } - pub fn multipart(mut self, multipart: Option) -> Self { + pub fn multipart(mut self, multipart: Option>) -> Self { self.multipart = multipart; self } diff --git a/src/model/application/command.rs b/src/model/application/command.rs index 27b2c3e2127..8930ba07fc1 100644 --- a/src/model/application/command.rs +++ b/src/model/application/command.rs @@ -159,7 +159,7 @@ impl Command { /// [`InteractionCreate`]: crate::client::EventHandler::interaction_create pub async fn create_global_command( cache_http: impl CacheHttp, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { builder.execute(cache_http, (None, None)).await } @@ -171,7 +171,7 @@ impl Command { /// Returns the same errors as [`Self::create_global_command`]. pub async fn set_global_commands( http: impl AsRef, - commands: Vec, + commands: &[CreateCommand<'_>], ) -> Result> { http.as_ref().create_global_commands(&commands).await } @@ -184,7 +184,7 @@ impl Command { pub async fn edit_global_command( cache_http: impl CacheHttp, command_id: CommandId, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { builder.execute(cache_http, (None, Some(command_id))).await } @@ -253,7 +253,6 @@ enum_number! { /// The parameters for an [`Command`]. /// /// [Discord docs](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). -#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] @@ -262,15 +261,15 @@ pub struct CommandOption { #[serde(rename = "type")] pub kind: CommandOptionType, /// The option name. - pub name: FixedString, + pub name: FixedString, /// Localizations of the option name with locale as the key #[serde(skip_serializing_if = "Option::is_none")] - pub name_localizations: Option>, + pub name_localizations: Option, FixedString>>, /// The option description. - pub description: FixedString, + pub description: FixedString, /// Localizations of the option description with locale as the key #[serde(skip_serializing_if = "Option::is_none")] - pub description_localizations: Option>, + pub description_localizations: Option, FixedString>>, /// Whether the parameter is optional or required. #[serde(default)] pub required: bool, @@ -281,7 +280,7 @@ pub struct CommandOption { /// [`String`]: CommandOptionType::String /// [`Integer`]: CommandOptionType::Integer #[serde(default)] - pub choices: Vec, + pub choices: FixedArray, /// The nested options. /// /// **Note**: Only available for [`SubCommand`] or [`SubCommandGroup`]. @@ -346,7 +345,7 @@ pub struct CommandOptionChoice { pub name: FixedString, /// Localizations of the choice name, with locale as key #[serde(skip_serializing_if = "Option::is_none")] - pub name_localizations: Option>, + pub name_localizations: Option>, /// The choice value. pub value: Value, } diff --git a/src/model/application/command_interaction.rs b/src/model/application/command_interaction.rs index 5428b7cbbe2..f8dacd0830e 100644 --- a/src/model/application/command_interaction.rs +++ b/src/model/application/command_interaction.rs @@ -116,7 +116,7 @@ impl CommandInteraction { pub async fn create_response( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponse, + builder: CreateInteractionResponse<'_>, ) -> Result<()> { builder.execute(cache_http, (self.id, &self.token)).await } @@ -133,7 +133,7 @@ impl CommandInteraction { pub async fn edit_response( &self, cache_http: impl CacheHttp, - builder: EditInteractionResponse, + builder: EditInteractionResponse<'_>, ) -> Result { builder.execute(cache_http, &self.token).await } @@ -162,7 +162,7 @@ impl CommandInteraction { pub async fn create_followup( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (None, &self.token)).await } @@ -180,7 +180,7 @@ impl CommandInteraction { &self, cache_http: impl CacheHttp, message_id: impl Into, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (Some(message_id.into()), &self.token)).await } @@ -246,7 +246,7 @@ impl CommandInteraction { pub async fn quick_modal( &self, ctx: &Context, - builder: CreateQuickModal, + builder: CreateQuickModal<'_>, ) -> Result> { builder.execute(ctx, self.id, &self.token).await } diff --git a/src/model/application/component_interaction.rs b/src/model/application/component_interaction.rs index b58c7810ff9..0f61aff0f2a 100644 --- a/src/model/application/component_interaction.rs +++ b/src/model/application/component_interaction.rs @@ -94,7 +94,7 @@ impl ComponentInteraction { pub async fn create_response( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponse, + builder: CreateInteractionResponse<'_>, ) -> Result<()> { builder.execute(cache_http, (self.id, &self.token)).await } @@ -111,7 +111,7 @@ impl ComponentInteraction { pub async fn edit_response( &self, cache_http: impl CacheHttp, - builder: EditInteractionResponse, + builder: EditInteractionResponse<'_>, ) -> Result { builder.execute(cache_http, &self.token).await } @@ -140,7 +140,7 @@ impl ComponentInteraction { pub async fn create_followup( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (None, &self.token)).await } @@ -158,7 +158,7 @@ impl ComponentInteraction { &self, cache_http: impl CacheHttp, message_id: impl Into, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (Some(message_id.into()), &self.token)).await } @@ -223,7 +223,7 @@ impl ComponentInteraction { pub async fn quick_modal( &self, ctx: &Context, - builder: CreateQuickModal, + builder: CreateQuickModal<'_>, ) -> Result> { builder.execute(ctx, self.id, &self.token).await } diff --git a/src/model/application/modal_interaction.rs b/src/model/application/modal_interaction.rs index e5091706363..a2701d8d365 100644 --- a/src/model/application/modal_interaction.rs +++ b/src/model/application/modal_interaction.rs @@ -85,7 +85,7 @@ impl ModalInteraction { pub async fn create_response( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponse, + builder: CreateInteractionResponse<'_>, ) -> Result<()> { builder.execute(cache_http, (self.id, &self.token)).await } @@ -102,7 +102,7 @@ impl ModalInteraction { pub async fn edit_response( &self, cache_http: impl CacheHttp, - builder: EditInteractionResponse, + builder: EditInteractionResponse<'_>, ) -> Result { builder.execute(cache_http, &self.token).await } @@ -131,7 +131,7 @@ impl ModalInteraction { pub async fn create_followup( &self, cache_http: impl CacheHttp, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (None, &self.token)).await } @@ -149,7 +149,7 @@ impl ModalInteraction { &self, cache_http: impl CacheHttp, message_id: impl Into, - builder: CreateInteractionResponseFollowup, + builder: CreateInteractionResponseFollowup<'_>, ) -> Result { builder.execute(cache_http, (Some(message_id.into()), &self.token)).await } diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index c5330b05bb4..1ddc7134518 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; #[cfg(feature = "model")] use std::sync::Arc; @@ -350,7 +351,7 @@ impl ChannelId { self, cache_http: impl CacheHttp, message_id: impl Into, - builder: EditMessage, + builder: EditMessage<'_>, ) -> Result { builder.execute(cache_http, (self, message_id.into(), None)).await } @@ -645,7 +646,7 @@ impl ChannelId { pub async fn say( self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, ) -> Result { let builder = CreateMessage::new().content(content); self.send_message(cache_http, builder).await @@ -717,11 +718,11 @@ impl ChannelId { /// reasons. /// /// [`File`]: tokio::fs::File - pub async fn send_files( + pub async fn send_files<'a>( self, cache_http: impl CacheHttp, - files: impl IntoIterator, - builder: CreateMessage, + files: impl IntoIterator>, + builder: CreateMessage<'a>, ) -> Result { self.send_message(cache_http, builder.files(files)).await } @@ -738,7 +739,7 @@ impl ChannelId { pub async fn send_message( self, cache_http: impl CacheHttp, - builder: CreateMessage, + builder: CreateMessage<'_>, ) -> Result { builder.execute(cache_http, (self, None)).await } diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index a50ba2ff9c1..8680dbe1e6b 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fmt; #[cfg(feature = "model")] use std::sync::Arc; @@ -501,7 +502,7 @@ impl GuildChannel { &self, cache_http: impl CacheHttp, message_id: impl Into, - builder: EditMessage, + builder: EditMessage<'_>, ) -> Result { self.id.edit_message(cache_http, message_id, builder).await } @@ -841,7 +842,7 @@ impl GuildChannel { pub async fn say( &self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, ) -> Result { self.id.say(cache_http, content).await } @@ -855,11 +856,11 @@ impl GuildChannel { /// See [`CreateMessage::execute`] for a list of possible errors, and their corresponding /// reasons. #[inline] - pub async fn send_files( + pub async fn send_files<'a>( self, cache_http: impl CacheHttp, - files: impl IntoIterator, - builder: CreateMessage, + files: impl IntoIterator>, + builder: CreateMessage<'a>, ) -> Result { self.send_message(cache_http, builder.files(files)).await } @@ -876,7 +877,7 @@ impl GuildChannel { pub async fn send_message( &self, cache_http: impl CacheHttp, - builder: CreateMessage, + builder: CreateMessage<'_>, ) -> Result { builder.execute(cache_http, (self.id, Some(self.guild_id))).await } diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index ad109361cce..17a8c1e05cb 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -1,5 +1,6 @@ //! Models relating to Discord channels. +use std::borrow::Cow; #[cfg(feature = "model")] use std::fmt::Display; #[cfg(all(feature = "cache", feature = "model"))] @@ -362,7 +363,11 @@ impl Message { /// Returns a [`ModelError::MessageTooLong`] if the message contents are too long. /// /// [Manage Messages]: Permissions::MANAGE_MESSAGES - pub async fn edit(&mut self, cache_http: impl CacheHttp, builder: EditMessage) -> Result<()> { + pub async fn edit( + &mut self, + cache_http: impl CacheHttp, + builder: EditMessage<'_>, + ) -> Result<()> { if let Some(flags) = self.flags { if flags.contains(MessageFlags::IS_VOICE_MESSAGE) { return Err(Error::Model(ModelError::CannotEditVoiceMessage)); @@ -602,7 +607,7 @@ impl Message { pub async fn reply( &self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, ) -> Result { self._reply(cache_http, content, Some(false)).await } @@ -626,7 +631,7 @@ impl Message { pub async fn reply_ping( &self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, ) -> Result { self._reply(cache_http, content, Some(true)).await } @@ -662,7 +667,7 @@ impl Message { async fn _reply( &self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, inlined: Option, ) -> Result { #[cfg(feature = "cache")] diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs index c053f1d9efc..d3c61081939 100644 --- a/src/model/channel/private_channel.rs +++ b/src/model/channel/private_channel.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fmt; #[cfg(feature = "model")] use std::sync::Arc; @@ -151,7 +152,7 @@ impl PrivateChannel { &self, cache_http: impl CacheHttp, message_id: impl Into, - builder: EditMessage, + builder: EditMessage<'_>, ) -> Result { self.id.edit_message(cache_http, message_id, builder).await } @@ -263,7 +264,7 @@ impl PrivateChannel { pub async fn say( &self, cache_http: impl CacheHttp, - content: impl Into, + content: impl Into>, ) -> Result { self.id.say(cache_http, content).await } @@ -279,11 +280,11 @@ impl PrivateChannel { /// /// [`CreateMessage::execute`]: ../../builder/struct.CreateMessage.html#method.execute #[inline] - pub async fn send_files( + pub async fn send_files<'a>( self, cache_http: impl CacheHttp, - files: impl IntoIterator, - builder: CreateMessage, + files: impl IntoIterator>, + builder: CreateMessage<'a>, ) -> Result { self.id.send_files(cache_http, files, builder).await } @@ -303,7 +304,7 @@ impl PrivateChannel { pub async fn send_message( &self, cache_http: impl CacheHttp, - builder: CreateMessage, + builder: CreateMessage<'_>, ) -> Result { self.id.send_message(cache_http, builder).await } diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs index 3afb9e77774..5b228ead7d7 100644 --- a/src/model/guild/guild_id.rs +++ b/src/model/guild/guild_id.rs @@ -170,7 +170,7 @@ impl GuildId { self, cache_http: impl CacheHttp, user_id: impl Into, - builder: AddMember, + builder: AddMember<'_>, ) -> Result> { builder.execute(cache_http, (self, user_id.into())).await } @@ -1523,7 +1523,7 @@ impl GuildId { pub async fn create_command( self, cache_http: impl CacheHttp, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { builder.execute(cache_http, (Some(self), None)).await } @@ -1536,7 +1536,7 @@ impl GuildId { pub async fn set_commands( self, http: impl AsRef, - commands: Vec, + commands: &[CreateCommand<'_>], ) -> Result> { http.as_ref().create_guild_commands(self, &commands).await } @@ -1552,7 +1552,7 @@ impl GuildId { self, cache_http: impl CacheHttp, command_id: CommandId, - builder: EditCommandPermissions, + builder: EditCommandPermissions<'_>, ) -> Result { builder.execute(cache_http, (self, command_id)).await } @@ -1600,7 +1600,7 @@ impl GuildId { self, cache_http: impl CacheHttp, command_id: CommandId, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { builder.execute(cache_http, (Some(self), Some(command_id))).await } diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index e46fb9341fe..e86c986bc3e 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -202,12 +202,12 @@ impl Member { /// /// [Moderate Members]: Permissions::MODERATE_MEMBERS #[doc(alias = "timeout")] - pub async fn disable_communication_until_datetime( + pub async fn disable_communication_until( &mut self, cache_http: impl CacheHttp, time: Timestamp, ) -> Result<()> { - let builder = EditMember::new().disable_communication_until_datetime(time); + let builder = EditMember::new().disable_communication_until(time); match self.guild_id.edit_member(cache_http, self.user.id, builder).await { Ok(_) => { self.communication_disabled_until = Some(time); diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index c17a7e999fe..eb0a81f6bbd 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -596,7 +596,7 @@ impl Guild { &self, cache_http: impl CacheHttp, user_id: impl Into, - builder: AddMember, + builder: AddMember<'_>, ) -> Result> { self.id.add_member(cache_http, user_id, builder).await } @@ -776,7 +776,7 @@ impl Guild { pub async fn create_command( &self, cache_http: impl CacheHttp, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { self.id.create_command(cache_http, builder).await } @@ -789,7 +789,7 @@ impl Guild { pub async fn set_commands( &self, http: impl AsRef, - commands: Vec, + commands: &[CreateCommand<'_>], ) -> Result> { self.id.set_commands(http, commands).await } @@ -807,7 +807,7 @@ impl Guild { &self, cache_http: impl CacheHttp, command_id: CommandId, - builder: EditCommandPermissions, + builder: EditCommandPermissions<'_>, ) -> Result { self.id.edit_command_permissions(cache_http, command_id, builder).await } @@ -857,7 +857,7 @@ impl Guild { &self, cache_http: impl CacheHttp, command_id: CommandId, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { self.id.edit_command(cache_http, command_id, builder).await } diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs index ea0f5ea7434..65e650b6fae 100644 --- a/src/model/guild/partial_guild.rs +++ b/src/model/guild/partial_guild.rs @@ -496,7 +496,7 @@ impl PartialGuild { pub async fn create_command( &self, cache_http: impl CacheHttp, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { self.id.create_command(cache_http, builder).await } @@ -509,7 +509,7 @@ impl PartialGuild { pub async fn set_commands( &self, http: impl AsRef, - commands: Vec, + commands: &[CreateCommand<'_>], ) -> Result> { self.id.set_commands(http, commands).await } @@ -527,7 +527,7 @@ impl PartialGuild { &self, cache_http: impl CacheHttp, command_id: CommandId, - builder: EditCommandPermissions, + builder: EditCommandPermissions<'_>, ) -> Result { self.id.edit_command_permissions(cache_http, command_id, builder).await } @@ -577,7 +577,7 @@ impl PartialGuild { &self, cache_http: impl CacheHttp, command_id: CommandId, - builder: CreateCommand, + builder: CreateCommand<'_>, ) -> Result { self.id.edit_command(cache_http, command_id, builder).await } diff --git a/src/model/user.rs b/src/model/user.rs index edd68898265..7d85270c5e3 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -176,7 +176,11 @@ impl CurrentUser { /// /// Returns an [`Error::Http`] if an invalid value is set. May also return an [`Error::Json`] /// if there is an error in deserializing the API response. - pub async fn edit(&mut self, cache_http: impl CacheHttp, builder: EditProfile) -> Result<()> { + pub async fn edit( + &mut self, + cache_http: impl CacheHttp, + builder: EditProfile<'_>, + ) -> Result<()> { *self = builder.execute(cache_http, ()).await?; Ok(()) } @@ -438,7 +442,7 @@ impl User { pub async fn direct_message( &self, cache_http: impl CacheHttp, - builder: CreateMessage, + builder: CreateMessage<'_>, ) -> Result { self.id.direct_message(cache_http, builder).await } @@ -446,7 +450,11 @@ impl User { /// This is an alias of [`Self::direct_message`]. #[allow(clippy::missing_errors_doc)] #[inline] - pub async fn dm(&self, cache_http: impl CacheHttp, builder: CreateMessage) -> Result { + pub async fn dm( + &self, + cache_http: impl CacheHttp, + builder: CreateMessage<'_>, + ) -> Result { self.direct_message(cache_http, builder).await } @@ -701,7 +709,7 @@ impl UserId { pub async fn direct_message( self, cache_http: impl CacheHttp, - builder: CreateMessage, + builder: CreateMessage<'_>, ) -> Result { self.create_dm_channel(&cache_http).await?.send_message(cache_http, builder).await } @@ -709,7 +717,11 @@ impl UserId { /// This is an alias of [`Self::direct_message`]. #[allow(clippy::missing_errors_doc)] #[inline] - pub async fn dm(self, cache_http: impl CacheHttp, builder: CreateMessage) -> Result { + pub async fn dm( + self, + cache_http: impl CacheHttp, + builder: CreateMessage<'_>, + ) -> Result { self.direct_message(cache_http, builder).await } diff --git a/src/model/webhook.rs b/src/model/webhook.rs index 4f5d01948b8..e13f84c6aa5 100644 --- a/src/model/webhook.rs +++ b/src/model/webhook.rs @@ -418,7 +418,7 @@ impl Webhook { &self, cache_http: impl CacheHttp, wait: bool, - builder: ExecuteWebhook, + builder: ExecuteWebhook<'_>, ) -> Result> { let token = self.token.as_ref().ok_or(ModelError::NoTokenSet)?.expose_secret(); builder.execute(cache_http, (self.id, token, wait)).await @@ -461,7 +461,7 @@ impl Webhook { &self, cache_http: impl CacheHttp, message_id: MessageId, - builder: EditWebhookMessage, + builder: EditWebhookMessage<'_>, ) -> Result { let token = self.token.as_ref().ok_or(ModelError::NoTokenSet)?.expose_secret(); builder.execute(cache_http, (self.id, token, message_id)).await diff --git a/src/utils/quick_modal.rs b/src/utils/quick_modal.rs index 0dd7f93fb6f..2b90308f6fb 100644 --- a/src/utils/quick_modal.rs +++ b/src/utils/quick_modal.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::builder::{ Builder as _, CreateActionRow, @@ -34,15 +36,15 @@ pub struct QuickModalResponse { /// ``` #[cfg(feature = "collector")] #[must_use] -pub struct CreateQuickModal { - title: String, +pub struct CreateQuickModal<'a> { + title: Cow<'a, str>, timeout: Option, - input_texts: Vec, + input_texts: Vec>, } #[cfg(feature = "collector")] -impl CreateQuickModal { - pub fn new(title: impl Into) -> Self { +impl<'a> CreateQuickModal<'a> { + pub fn new(title: impl Into>) -> Self { Self { title: title.into(), timeout: None, @@ -63,7 +65,7 @@ impl CreateQuickModal { /// /// As the `custom_id` field of [`CreateInputText`], just supply an empty string. All custom /// IDs are overwritten by [`CreateQuickModal`] when sending the modal. - pub fn field(mut self, input_text: CreateInputText) -> Self { + pub fn field(mut self, input_text: CreateInputText<'a>) -> Self { self.input_texts.push(input_text); self } @@ -71,14 +73,14 @@ impl CreateQuickModal { /// Convenience method to add a single-line input text field. /// /// Wraps [`Self::field`]. - pub fn short_field(self, label: impl Into) -> Self { + pub fn short_field(self, label: impl Into>) -> Self { self.field(CreateInputText::new(InputTextStyle::Short, label, "")) } /// Convenience method to add a multi-line input text field. /// /// Wraps [`Self::field`]. - pub fn paragraph_field(self, label: impl Into) -> Self { + pub fn paragraph_field(self, label: impl Into>) -> Self { self.field(CreateInputText::new(InputTextStyle::Paragraph, label, "")) } @@ -100,7 +102,7 @@ impl CreateQuickModal { .map(|(i, input_text)| { CreateActionRow::InputText(input_text.custom_id(i.to_string())) }) - .collect(), + .collect::>(), ), ); builder.execute(ctx, (interaction_id, token)).await?;