serenity/builder/
edit_role.rs

1#[cfg(feature = "http")]
2use super::Builder;
3use super::CreateAttachment;
4#[cfg(feature = "http")]
5use crate::http::CacheHttp;
6#[cfg(feature = "http")]
7use crate::internal::prelude::*;
8use crate::model::prelude::*;
9
10/// A builder to create or edit a [`Role`] for use via a number of model methods.
11///
12/// These are:
13///
14/// - [`PartialGuild::create_role`]
15/// - [`PartialGuild::edit_role`]
16/// - [`Guild::create_role`]
17/// - [`Guild::edit_role`]
18/// - [`GuildId::create_role`]
19/// - [`GuildId::edit_role`]
20/// - [`Role::edit`]
21///
22/// Defaults are provided for each parameter on role creation.
23///
24/// # Examples
25///
26/// Create a hoisted, mentionable role named `"a test role"`:
27///
28/// ```rust,no_run
29/// # use serenity::builder::EditRole;
30/// # use serenity::http::Http;
31/// # use serenity::model::id::GuildId;
32/// # use std::sync::Arc;
33/// #
34/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
35/// # let http: Arc<Http> = unimplemented!();
36/// # let guild_id: GuildId = unimplemented!();
37/// #
38/// // assuming a `guild_id` has been bound
39/// let builder = EditRole::new().name("a test role").hoist(true).mentionable(true);
40/// let role = guild_id.create_role(&http, builder).await?;
41/// # Ok(())
42/// # }
43/// ```
44///
45/// [Discord docs](https://discord.com/developers/docs/resources/guild#modify-guild-role)
46#[derive(Clone, Debug, Default, Serialize)]
47#[must_use]
48pub struct EditRole<'a> {
49    #[serde(skip_serializing_if = "Option::is_none")]
50    name: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    permissions: Option<u64>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    #[serde(rename = "color")]
55    colour: Option<Colour>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    hoist: Option<bool>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    icon: Option<Option<String>>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    unicode_emoji: Option<Option<String>>,
62
63    #[serde(skip_serializing_if = "Option::is_none")]
64    mentionable: Option<bool>,
65
66    #[serde(skip)]
67    position: Option<u16>,
68    #[serde(skip)]
69    audit_log_reason: Option<&'a str>,
70}
71
72impl<'a> EditRole<'a> {
73    /// Equivalent to [`Self::default`].
74    pub fn new() -> Self {
75        Self::default()
76    }
77
78    /// Creates a new builder with the values of the given [`Role`].
79    pub fn from_role(role: &Role) -> Self {
80        EditRole {
81            hoist: Some(role.hoist),
82            mentionable: Some(role.mentionable),
83            name: Some(role.name.clone()),
84            permissions: Some(role.permissions.bits()),
85            position: Some(role.position),
86            colour: Some(role.colour),
87            unicode_emoji: role.unicode_emoji.as_ref().map(|v| Some(v.clone())),
88            audit_log_reason: None,
89            // TODO: Do we want to download role.icon?
90            icon: None,
91        }
92    }
93
94    /// Set the colour of the role.
95    pub fn colour(mut self, colour: impl Into<Colour>) -> Self {
96        self.colour = Some(colour.into());
97        self
98    }
99
100    /// Whether or not to hoist the role above lower-positioned roles in the user list.
101    pub fn hoist(mut self, hoist: bool) -> Self {
102        self.hoist = Some(hoist);
103        self
104    }
105
106    /// Whether or not to make the role mentionable, upon which users with that role will be
107    /// notified.
108    pub fn mentionable(mut self, mentionable: bool) -> Self {
109        self.mentionable = Some(mentionable);
110        self
111    }
112
113    /// Set the role's name.
114    pub fn name(mut self, name: impl Into<String>) -> Self {
115        self.name = Some(name.into());
116        self
117    }
118
119    /// Set the role's permissions.
120    pub fn permissions(mut self, permissions: Permissions) -> Self {
121        self.permissions = Some(permissions.bits());
122        self
123    }
124
125    /// Set the role's position in the role list. This correlates to the role's position in the
126    /// user list.
127    pub fn position(mut self, position: u16) -> Self {
128        self.position = Some(position);
129        self
130    }
131
132    /// Set the role icon to a unicode emoji.
133    pub fn unicode_emoji(mut self, unicode_emoji: Option<String>) -> Self {
134        self.unicode_emoji = Some(unicode_emoji);
135        self.icon = Some(None);
136        self
137    }
138
139    /// Set the role icon to a custom image.
140    pub fn icon(mut self, icon: Option<&CreateAttachment>) -> Self {
141        self.icon = Some(icon.map(CreateAttachment::to_base64));
142        self.unicode_emoji = Some(None);
143        self
144    }
145
146    /// Sets the request's audit log reason.
147    pub fn audit_log_reason(mut self, reason: &'a str) -> Self {
148        self.audit_log_reason = Some(reason);
149        self
150    }
151}
152
153#[cfg(feature = "http")]
154#[async_trait::async_trait]
155impl Builder for EditRole<'_> {
156    type Context<'ctx> = (GuildId, Option<RoleId>);
157    type Built = Role;
158
159    /// Edits the role.
160    ///
161    /// **Note**: Requires the [Manage Roles] permission.
162    ///
163    /// # Errors
164    ///
165    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
166    /// lacks permission. Otherwise returns [`Error::Http`], as well as if invalid data is given.
167    ///
168    /// [Manage Roles]: Permissions::MANAGE_ROLES
169    async fn execute(
170        self,
171        cache_http: impl CacheHttp,
172        ctx: Self::Context<'_>,
173    ) -> Result<Self::Built> {
174        let (guild_id, role_id) = ctx;
175
176        #[cfg(feature = "cache")]
177        crate::utils::user_has_guild_perms(&cache_http, guild_id, Permissions::MANAGE_ROLES)?;
178
179        let http = cache_http.http();
180        let role = match role_id {
181            Some(role_id) => {
182                http.edit_role(guild_id, role_id, &self, self.audit_log_reason).await?
183            },
184            None => http.create_role(guild_id, &self, self.audit_log_reason).await?,
185        };
186
187        if let Some(position) = self.position {
188            http.edit_role_position(guild_id, role.id, position, self.audit_log_reason).await?;
189        }
190        Ok(role)
191    }
192}