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