Skip to content

Commit

Permalink
Change Embed::fields to use FixedArray (serenity-rs#2674)
Browse files Browse the repository at this point in the history
`Embed::fields` previously had to stay as a `Vec` due to `CreateEmbed` wrapping
around it, but by implementing `Serialize` manually we can overwrite the
`Embed::fields` with a normal `Vec`, for a small performance hit on
serialization while saving some space for all stored `Embed`s.
  • Loading branch information
GnomedDev authored and mkrasnitski committed Jul 28, 2024
1 parent 864b3c9 commit cd5da84
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 36 deletions.
87 changes: 53 additions & 34 deletions src/builder/create_embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,22 @@ 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, Serialize, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
#[must_use]
pub struct CreateEmbed(Embed);
pub struct CreateEmbed {
inner: Embed,
fields: Vec<EmbedField>,
}

impl serde::Serialize for CreateEmbed {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
let owned_self = self.clone();

let mut embed = owned_self.inner;
embed.fields = owned_self.fields.into();
embed.serialize(serializer)
}
}

impl CreateEmbed {
/// Equivalent to [`Self::default`].
Expand All @@ -35,7 +48,7 @@ impl CreateEmbed {
///
/// Refer to the documentation for [`CreateEmbedAuthor`] for more information.
pub fn author(mut self, author: CreateEmbedAuthor) -> Self {
self.0.author = Some(author.0);
self.inner.author = Some(author.0);
self
}

Expand All @@ -50,7 +63,7 @@ impl CreateEmbed {
/// Set the colour of the left-hand side of the embed.
#[inline]
pub fn colour<C: Into<Colour>>(mut self, colour: C) -> Self {
self.0.colour = Some(colour.into());
self.inner.colour = Some(colour.into());
self
}

Expand All @@ -59,7 +72,7 @@ impl CreateEmbed {
/// **Note**: This can't be longer than 4096 characters.
#[inline]
pub fn description(mut self, description: impl Into<String>) -> Self {
self.0.description = Some(description.into().into());
self.inner.description = Some(description.into().into());
self
}

Expand All @@ -74,7 +87,7 @@ impl CreateEmbed {
value: impl Into<String>,
inline: bool,
) -> Self {
self.0.fields.push(EmbedField::new(name, value, inline));
self.fields.push(EmbedField::new(name, value, inline));
self
}

Expand All @@ -88,22 +101,22 @@ impl CreateEmbed {
{
let fields =
fields.into_iter().map(|(name, value, inline)| EmbedField::new(name, value, inline));
self.0.fields.extend(fields);
self.fields.extend(fields);
self
}

/// Set the footer of the embed.
///
/// Refer to the documentation for [`CreateEmbedFooter`] for more information.
pub fn footer(mut self, footer: CreateEmbedFooter) -> Self {
self.0.footer = Some(footer.0);
self.inner.footer = Some(footer.0);
self
}

/// Set the image associated with the embed. This only supports HTTP(S).
#[inline]
pub fn image(mut self, url: impl Into<String>) -> Self {
self.0.image = Some(EmbedImage {
self.inner.image = Some(EmbedImage {
url: url.into().into(),
proxy_url: None,
height: None,
Expand All @@ -115,7 +128,7 @@ impl CreateEmbed {
/// Set the thumbnail of the embed. This only supports HTTP(S).
#[inline]
pub fn thumbnail(mut self, url: impl Into<String>) -> Self {
self.0.thumbnail = Some(EmbedThumbnail {
self.inner.thumbnail = Some(EmbedThumbnail {
url: url.into().into(),
proxy_url: None,
height: None,
Expand All @@ -140,21 +153,21 @@ impl CreateEmbed {
/// ```
#[inline]
pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
self.0.timestamp = Some(timestamp.into());
self.inner.timestamp = Some(timestamp.into());
self
}

/// Set the title of the embed.
#[inline]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.0.title = Some(title.into().into());
self.inner.title = Some(title.into().into());
self
}

/// Set the URL to direct to when clicking on the title.
#[inline]
pub fn url(mut self, url: impl Into<String>) -> Self {
self.0.url = Some(url.into().into());
self.inner.url = Some(url.into().into());
self
}

Expand All @@ -174,24 +187,24 @@ impl CreateEmbed {
#[cfg(feature = "http")]
pub(super) fn check_length(&self) -> Result<()> {
let mut length = 0;
if let Some(ref author) = self.0.author {
if let Some(ref author) = self.inner.author {
length += author.name.chars().count();
}

if let Some(ref description) = self.0.description {
if let Some(ref description) = self.inner.description {
length += description.chars().count();
}

for field in &self.0.fields {
for field in &self.fields {
length += field.name.chars().count();
length += field.value.chars().count();
}

if let Some(ref footer) = self.0.footer {
if let Some(ref footer) = self.inner.footer {
length += footer.text.chars().count();
}

if let Some(ref title) = self.0.title {
if let Some(ref title) = self.inner.title {
length += title.chars().count();
}

Expand All @@ -203,27 +216,33 @@ impl CreateEmbed {
impl Default for CreateEmbed {
/// Creates a builder with default values, setting the `type` to `rich`.
fn default() -> Self {
Self(Embed {
Self {
fields: Vec::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,
})
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,
},
}
}
}

impl From<Embed> for CreateEmbed {
fn from(embed: Embed) -> Self {
Self(embed)
fn from(mut embed: Embed) -> Self {
Self {
fields: std::mem::take(&mut embed.fields).into_vec(),
inner: embed,
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/model/channel/embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ pub struct Embed {
///
/// The maximum number of fields is 25.
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub fields: Vec<EmbedField>,
#[serde(skip_serializing_if = "FixedArray::is_empty")]
pub fields: FixedArray<EmbedField, u8>,
}

/// An author object in an embed.
Expand Down

0 comments on commit cd5da84

Please sign in to comment.