Skip to main content

twilight_http/request/guild/role/
update_role.rs

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