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