Skip to main content

twilight_http/request/guild/role/
update_role.rs

1use crate::{
2    client::Client,
3    error::Error,
4    request::{self, AuditLogReason, Nullable, Request, TryIntoRequest},
5    response::{Response, ResponseFuture},
6    routing::Route,
7};
8use serde::Serialize;
9use std::future::IntoFuture;
10use twilight_model::{
11    guild::{Permissions, Role, RoleColors},
12    id::{
13        Id,
14        marker::{GuildMarker, RoleMarker},
15    },
16};
17use twilight_validate::request::{ValidationError, audit_reason as validate_audit_reason};
18
19#[derive(Serialize)]
20struct UpdateRoleFields<'a> {
21    #[serde(skip_serializing_if = "Option::is_none")]
22    color: Option<Nullable<u32>>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    colors: Option<Nullable<RoleColors>>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    hoist: Option<bool>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    icon: Option<Nullable<&'a str>>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    mentionable: Option<bool>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    name: Option<Nullable<&'a str>>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    permissions: Option<Permissions>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    unicode_emoji: Option<Nullable<&'a str>>,
37}
38
39/// Update a role by guild id and its id.
40#[must_use = "requests must be configured and executed"]
41pub struct UpdateRole<'a> {
42    fields: UpdateRoleFields<'a>,
43    guild_id: Id<GuildMarker>,
44    http: &'a Client,
45    role_id: Id<RoleMarker>,
46    reason: Result<Option<&'a str>, ValidationError>,
47}
48
49impl<'a> UpdateRole<'a> {
50    pub(crate) const fn new(
51        http: &'a Client,
52        guild_id: Id<GuildMarker>,
53        role_id: Id<RoleMarker>,
54    ) -> Self {
55        Self {
56            fields: UpdateRoleFields {
57                color: None,
58                colors: None,
59                hoist: None,
60                icon: None,
61                mentionable: None,
62                name: None,
63                permissions: None,
64                unicode_emoji: None,
65            },
66            guild_id,
67            http,
68            role_id,
69            reason: Ok(None),
70        }
71    }
72
73    /// Set the role color.
74    ///
75    /// This must be a valid hexadecimal RGB value. `0x000000` is ignored and
76    /// doesn't count towards the final computed color in the user list. Refer
77    /// to [`COLOR_MAXIMUM`] for the maximum acceptable value.
78    ///
79    /// [`COLOR_MAXIMUM`]: twilight_validate::embed::COLOR_MAXIMUM
80    pub const fn color(mut self, color: Option<u32>) -> Self {
81        self.fields.color = Some(Nullable(color));
82
83        self
84    }
85
86    /// Set the role colours.
87    pub const fn colors(mut self, colors: Option<RoleColors>) -> Self {
88        self.fields.colors = Some(Nullable(colors));
89
90        self
91    }
92
93    /// If true, display the role in the members list.
94    pub const fn hoist(mut self, hoist: bool) -> Self {
95        self.fields.hoist = Some(hoist);
96
97        self
98    }
99
100    /// Set the icon of the role.
101    ///
102    /// Only works if the guild has the `ROLE_ICONS` feature.
103    ///
104    /// See [Discord Docs/Image Data].
105    ///
106    /// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
107    ///
108    /// # Editing
109    ///
110    /// Pass [`None`] to clear the existing icon.
111    ///
112    /// **Warning**: If the existing unicode emoji isn't cleared when setting the icon, it might
113    /// cause incorrect behavior.
114    ///
115    /// # Examples
116    ///
117    /// Sets a role icon. The unicode emoji should always be cleared to ensure the icon can be
118    /// set correctly.
119    ///
120    /// ```no_run
121    /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
122    /// use twilight_http::Client;
123    /// use twilight_model::id::Id;
124    ///
125    /// let client = Client::new("token".to_owned());
126    /// let guild_id = Id::new(1);
127    /// let role_id = Id::new(1);
128    /// let icon = "data:image/png;base64,BASE64_ENCODED_PNG_IMAGE_DATA";
129    ///
130    /// client
131    ///     .update_role(guild_id, role_id)
132    ///     .icon(Some(icon))
133    ///     .unicode_emoji(None)
134    ///     .await?;
135    /// # Ok(()) }
136    /// ```
137    pub const fn icon(mut self, icon: Option<&'a str>) -> Self {
138        self.fields.icon = Some(Nullable(icon));
139
140        self
141    }
142
143    /// If true, the role can be @mentioned (pinged) in chat.
144    pub const fn mentionable(mut self, mentionable: bool) -> Self {
145        self.fields.mentionable = Some(mentionable);
146
147        self
148    }
149
150    /// Set the name of the role.
151    pub const fn name(mut self, name: Option<&'a str>) -> Self {
152        self.fields.name = Some(Nullable(name));
153
154        self
155    }
156
157    /// Set the allowed permissions of this role.
158    pub const fn permissions(mut self, permissions: Permissions) -> Self {
159        self.fields.permissions = Some(permissions);
160
161        self
162    }
163
164    /// Set the unicode emoji of a role.
165    ///
166    /// Only works if the guild has the `ROLE_ICONS` feature.
167    ///
168    /// # Editing
169    ///
170    /// Pass [`None`] to clear the existing unicode emoji.
171    ///
172    /// **Warning**: If the existing icon isn't cleared when setting the unicode emoji, it might
173    /// cause incorrect behavior.
174    ///
175    /// # Examples
176    ///
177    /// Sets a role unicode emoji. The icon should always be cleared to ensure the unicode emoji
178    /// can be set correctly.
179    ///
180    /// ```no_run
181    /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
182    /// use twilight_http::Client;
183    /// use twilight_model::id::Id;
184    ///
185    /// let client = Client::new("token".to_owned());
186    /// let guild_id = Id::new(1);
187    /// let role_id = Id::new(1);
188    ///
189    /// client
190    ///     .update_role(guild_id, role_id)
191    ///     .icon(None)
192    ///     .unicode_emoji(Some("🦀"))
193    ///     .await?;
194    /// # Ok(()) }
195    /// ```
196    pub const fn unicode_emoji(mut self, unicode_emoji: Option<&'a str>) -> Self {
197        self.fields.unicode_emoji = Some(Nullable(unicode_emoji));
198
199        self
200    }
201}
202
203impl<'a> AuditLogReason<'a> for UpdateRole<'a> {
204    fn reason(mut self, reason: &'a str) -> Self {
205        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
206
207        self
208    }
209}
210
211impl IntoFuture for UpdateRole<'_> {
212    type Output = Result<Response<Role>, Error>;
213
214    type IntoFuture = ResponseFuture<Role>;
215
216    fn into_future(self) -> Self::IntoFuture {
217        let http = self.http;
218
219        match self.try_into_request() {
220            Ok(request) => http.request(request),
221            Err(source) => ResponseFuture::error(source),
222        }
223    }
224}
225
226impl TryIntoRequest for UpdateRole<'_> {
227    fn try_into_request(self) -> Result<Request, Error> {
228        let mut request = Request::builder(&Route::UpdateRole {
229            guild_id: self.guild_id.get(),
230            role_id: self.role_id.get(),
231        });
232
233        request = request.json(&self.fields);
234
235        if let Some(reason) = self.reason.map_err(Error::validation)? {
236            request = request.headers(request::audit_header(reason)?);
237        }
238
239        request.build()
240    }
241}