Skip to main content

twilight_http/request/guild/emoji/
create_emoji.rs

1#[cfg(not(target_os = "wasi"))]
2use crate::response::{Response, ResponseFuture};
3use crate::{
4    client::Client,
5    error::Error,
6    request::{self, AuditLogReason, Request, TryIntoRequest},
7    routing::Route,
8};
9use serde::Serialize;
10use std::future::IntoFuture;
11use twilight_model::{
12    guild::Emoji,
13    id::{
14        Id,
15        marker::{GuildMarker, RoleMarker},
16    },
17};
18use twilight_validate::request::{ValidationError, audit_reason as validate_audit_reason};
19
20#[derive(Serialize)]
21struct CreateEmojiFields<'a> {
22    image: &'a str,
23    name: &'a str,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    roles: Option<&'a [Id<RoleMarker>]>,
26}
27
28/// Create an emoji in a guild.
29///
30/// The emoji must be a Data URI, in the form of
31/// `data:image/{type};base64,{data}` where `{type}` is the image MIME type and
32/// `{data}` is the base64-encoded image. See [Discord Docs/Image Data].
33///
34/// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
35#[must_use = "requests must be configured and executed"]
36pub struct CreateEmoji<'a> {
37    fields: CreateEmojiFields<'a>,
38    guild_id: Id<GuildMarker>,
39    http: &'a Client,
40    reason: Result<Option<&'a str>, ValidationError>,
41}
42
43impl<'a> CreateEmoji<'a> {
44    pub(crate) const fn new(
45        http: &'a Client,
46        guild_id: Id<GuildMarker>,
47        name: &'a str,
48        image: &'a str,
49    ) -> Self {
50        Self {
51            fields: CreateEmojiFields {
52                image,
53                name,
54                roles: None,
55            },
56            guild_id,
57            http,
58            reason: Ok(None),
59        }
60    }
61
62    /// Whitelist roles for this emoji.
63    ///
64    /// See [Discord Docs/Emoji Object].
65    ///
66    /// [Discord Docs/Emoji Object]: https://discord.com/developers/docs/resources/emoji#emoji-object-emoji-structure
67    pub const fn roles(mut self, roles: &'a [Id<RoleMarker>]) -> Self {
68        self.fields.roles = Some(roles);
69
70        self
71    }
72}
73
74impl<'a> AuditLogReason<'a> for CreateEmoji<'a> {
75    fn reason(mut self, reason: &'a str) -> Self {
76        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
77
78        self
79    }
80}
81
82#[cfg(not(target_os = "wasi"))]
83impl IntoFuture for CreateEmoji<'_> {
84    type Output = Result<Response<Emoji>, Error>;
85
86    type IntoFuture = ResponseFuture<Emoji>;
87
88    fn into_future(self) -> Self::IntoFuture {
89        let http = self.http;
90
91        match self.try_into_request() {
92            Ok(request) => http.request(request),
93            Err(source) => ResponseFuture::error(source),
94        }
95    }
96}
97
98impl TryIntoRequest for CreateEmoji<'_> {
99    fn try_into_request(self) -> Result<Request, Error> {
100        let mut request = Request::builder(&Route::CreateEmoji {
101            guild_id: self.guild_id.get(),
102        });
103
104        request = request.json(&self.fields);
105
106        if let Some(reason) = self.reason.map_err(Error::validation)? {
107            request = request.headers(request::audit_header(reason)?);
108        }
109
110        request.build()
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use std::error::Error;
118
119    #[test]
120    fn test_create_emoji() -> Result<(), Box<dyn Error>> {
121        const GUILD_ID: Id<GuildMarker> = Id::new(1);
122        const ROLE_ID: Id<RoleMarker> = Id::new(2);
123
124        let client = Client::new("token".into());
125
126        {
127            let expected = r#"{"image":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI","name":"square"}"#;
128            let actual = CreateEmoji::new(
129                &client,
130                GUILD_ID,
131                "square",
132                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI",
133            )
134            .try_into_request()?;
135
136            assert_eq!(Some(expected.as_bytes()), actual.body());
137        }
138
139        {
140            let expected = r#"{"image":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI","name":"square","roles":["2"]}"#;
141            let actual = CreateEmoji::new(
142                &client,
143                GUILD_ID,
144                "square",
145                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI",
146            )
147            .roles(&[ROLE_ID])
148            .try_into_request()?;
149
150            assert_eq!(Some(expected.as_bytes()), actual.body());
151        }
152        Ok(())
153    }
154}