twilight_http/request/guild/role/
create_role.rs

1use 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::{Permissions, Role},
12    id::{marker::GuildMarker, Id},
13};
14use twilight_validate::request::{audit_reason as validate_audit_reason, ValidationError};
15
16#[derive(Serialize)]
17struct CreateRoleFields<'a> {
18    #[serde(skip_serializing_if = "Option::is_none")]
19    color: Option<u32>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    hoist: Option<bool>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    icon: Option<&'a [u8]>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    mentionable: Option<bool>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    name: Option<&'a str>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    permissions: Option<Permissions>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    unicode_emoji: Option<&'a str>,
32}
33
34/// Create a role in a guild.
35///
36/// # Examples
37///
38/// ```no_run
39/// use twilight_http::Client;
40/// use twilight_model::id::Id;
41///
42/// # #[tokio::main]
43/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
44/// let client = Client::new("my token".to_owned());
45/// let guild_id = Id::new(234);
46///
47/// client
48///     .create_role(guild_id)
49///     .color(0xd90083)
50///     .name("Bright Pink")
51///     .await?;
52/// # Ok(()) }
53/// ```
54#[must_use = "requests must be configured and executed"]
55pub struct CreateRole<'a> {
56    fields: CreateRoleFields<'a>,
57    guild_id: Id<GuildMarker>,
58    http: &'a Client,
59    reason: Result<Option<&'a str>, ValidationError>,
60}
61
62impl<'a> CreateRole<'a> {
63    pub(crate) const fn new(http: &'a Client, guild_id: Id<GuildMarker>) -> Self {
64        Self {
65            fields: CreateRoleFields {
66                color: None,
67                hoist: None,
68                icon: None,
69                mentionable: None,
70                name: None,
71                permissions: None,
72                unicode_emoji: None,
73            },
74            guild_id,
75            http,
76            reason: Ok(None),
77        }
78    }
79
80    /// Set the role color.
81    ///
82    /// This must be a valid hexadecimal RGB value. `0x000000` is ignored and
83    /// doesn't count towards the final computed color in the user list. Refer
84    /// to [`COLOR_MAXIMUM`] for the maximum acceptable value.
85    ///
86    /// [`COLOR_MAXIMUM`]: twilight_validate::embed::COLOR_MAXIMUM
87    pub const fn color(mut self, color: u32) -> Self {
88        self.fields.color = Some(color);
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    pub const fn icon(mut self, icon: &'a [u8]) -> Self {
108        self.fields.icon = Some(icon);
109
110        self
111    }
112
113    /// If true, the role can be @mentioned (pinged) in chat.
114    pub const fn mentionable(mut self, mentionable: bool) -> Self {
115        self.fields.mentionable = Some(mentionable);
116
117        self
118    }
119
120    /// Set the name of the role.
121    ///
122    /// If none is specified, Discord sets this to `New Role`.
123    pub const fn name(mut self, name: &'a str) -> Self {
124        self.fields.name = Some(name);
125
126        self
127    }
128
129    /// Set the allowed permissions of this role.
130    pub const fn permissions(mut self, permissions: Permissions) -> Self {
131        self.fields.permissions = Some(permissions);
132
133        self
134    }
135
136    /// Set the unicode emoji of a role.
137    pub const fn unicode_emoji(mut self, unicode_emoji: &'a str) -> Self {
138        self.fields.unicode_emoji = Some(unicode_emoji);
139
140        self
141    }
142}
143
144impl<'a> AuditLogReason<'a> for CreateRole<'a> {
145    fn reason(mut self, reason: &'a str) -> Self {
146        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
147
148        self
149    }
150}
151
152impl IntoFuture for CreateRole<'_> {
153    type Output = Result<Response<Role>, Error>;
154
155    type IntoFuture = ResponseFuture<Role>;
156
157    fn into_future(self) -> Self::IntoFuture {
158        let http = self.http;
159
160        match self.try_into_request() {
161            Ok(request) => http.request(request),
162            Err(source) => ResponseFuture::error(source),
163        }
164    }
165}
166
167impl TryIntoRequest for CreateRole<'_> {
168    fn try_into_request(self) -> Result<Request, Error> {
169        let mut request = Request::builder(&Route::CreateRole {
170            guild_id: self.guild_id.get(),
171        });
172
173        request = request.json(&self.fields);
174
175        if let Some(reason) = self.reason.map_err(Error::validation)? {
176            request = request.headers(request::audit_header(reason)?);
177        }
178
179        request.build()
180    }
181}