twilight_model/guild/
role.rs

1use super::RoleColors;
2use super::{RoleFlags, RoleTags};
3use crate::{
4    guild::Permissions,
5    id::{Id, marker::RoleMarker},
6    util::image_hash::ImageHash,
7};
8use serde::{Deserialize, Serialize};
9use std::cmp::Ordering;
10
11#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
12pub struct Role {
13    #[deprecated(
14        since = "0.17.0",
15        note = "Deprecated by Discord in favour for the new ``colors`` field."
16    )]
17    pub color: u32,
18    pub colors: RoleColors,
19    pub hoist: bool,
20    /// Icon image hash.
21    ///
22    /// Present if the guild has the `ROLE_ICONS` feature and if the role has
23    /// one.
24    ///
25    /// See [Discord Docs/Image Formatting].
26    ///
27    /// [Discord Docs/Image Formatting]: https://discord.com/developers/docs/reference#image-formatting
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub icon: Option<ImageHash>,
30    pub id: Id<RoleMarker>,
31    pub managed: bool,
32    pub mentionable: bool,
33    pub name: String,
34    pub permissions: Permissions,
35    pub position: i64,
36    /// Flags for this role.
37    pub flags: RoleFlags,
38    /// Tags about the role.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub tags: Option<RoleTags>,
41    /// Icon unicode emoji.
42    ///
43    /// Present if the guild has the `ROLE_ICONS` feature and if the role has
44    /// one.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub unicode_emoji: Option<String>,
47}
48
49impl Ord for Role {
50    /// Compare two roles to each other using their position and ID.
51    ///
52    /// Roles are primarily ordered by their position in descending order. For example,
53    /// a role with a position of 17 is considered a higher role than one with a
54    /// position of 12.
55    ///
56    /// Discord does not guarantee that role positions are positive, unique, or contiguous. When
57    /// two or more roles have the same position then the order is based on the roles' IDs in
58    /// ascending order. For example, given two roles with positions of 10 then a role
59    /// with an ID of 1 would be considered a higher role than one with an ID of 20.
60    ///
61    /// ### Examples
62    ///
63    /// Compare the position of two roles:
64    ///
65    /// ```
66    /// # use twilight_model::{guild::{Permissions, Role, RoleFlags}, id::Id};
67    /// # use std::cmp::Ordering;
68    /// # use twilight_model::guild::RoleColors;
69    /// let role_a = Role {
70    ///     id: Id::new(123),
71    ///     position: 12,
72    /// #   color: 0,
73    /// #   colors: RoleColors {
74    /// #       primary_color: 0,
75    /// #       secondary_color: None,
76    /// #       tertiary_color: None,
77    /// #   },
78    /// #   hoist: true,
79    /// #   icon: None,
80    /// #   managed: false,
81    /// #   mentionable: true,
82    /// #   name: "test".to_owned(),
83    /// #   permissions: Permissions::ADMINISTRATOR,
84    /// #   flags: RoleFlags::empty(),
85    /// #   tags: None,
86    /// #   unicode_emoji: None,
87    ///     // ...
88    /// };
89    /// let role_b = Role {
90    ///     id: Id::new(456),
91    ///     position: 13,
92    /// #   #[allow(deprecated)]
93    /// #   color: 0,
94    /// #   colors: RoleColors {
95    /// #       primary_color: 0,
96    /// #       secondary_color: None,
97    /// #       tertiary_color: None,
98    /// #   },
99    /// #   hoist: true,
100    /// #   icon: None,
101    /// #   managed: false,
102    /// #   mentionable: true,
103    /// #   name: "test".to_owned(),
104    /// #   permissions: Permissions::ADMINISTRATOR,
105    /// #   flags: RoleFlags::empty(),
106    /// #   tags: None,
107    /// #   unicode_emoji: None,
108    ///     // ...
109    /// };
110    /// assert_eq!(Ordering::Less, role_a.cmp(&role_b));
111    /// assert_eq!(Ordering::Greater, role_b.cmp(&role_a));
112    /// assert_eq!(Ordering::Equal, role_a.cmp(&role_a));
113    /// assert_eq!(Ordering::Equal, role_b.cmp(&role_b));
114    /// ```
115    ///
116    /// Compare the position of two roles with the same position:
117    ///
118    /// ```
119    /// # use twilight_model::{guild::{Permissions, Role, RoleFlags}, id::Id};
120    /// # use std::cmp::Ordering;
121    /// # use twilight_model::guild::RoleColors;
122    /// let role_a = Role {
123    ///     id: Id::new(123),
124    ///     position: 12,
125    /// #   color: 0,
126    /// #   colors: RoleColors {
127    /// #       primary_color: 0,
128    /// #       secondary_color: None,
129    /// #       tertiary_color: None,
130    /// #   },
131    /// #   hoist: true,
132    /// #   icon: None,
133    /// #   managed: false,
134    /// #   mentionable: true,
135    /// #   name: "test".to_owned(),
136    /// #   permissions: Permissions::ADMINISTRATOR,
137    /// #   flags: RoleFlags::empty(),
138    /// #   tags: None,
139    /// #   unicode_emoji: None,
140    /// };
141    /// let role_b = Role {
142    ///     id: Id::new(456),
143    ///     position: 12,
144    /// #   color: 0,
145    /// #   colors: RoleColors {
146    /// #       primary_color: 0,
147    /// #       secondary_color: None,
148    /// #       tertiary_color: None,
149    /// #   },
150    /// #   hoist: true,
151    /// #   icon: None,
152    /// #   managed: false,
153    /// #   mentionable: true,
154    /// #   name: "test".to_owned(),
155    /// #   permissions: Permissions::ADMINISTRATOR,
156    /// #   flags: RoleFlags::empty(),
157    /// #   tags: None,
158    /// #   unicode_emoji: None,
159    /// };
160    /// assert_eq!(Ordering::Greater, role_a.cmp(&role_b));
161    /// assert_eq!(Ordering::Less, role_b.cmp(&role_a));
162    /// assert_eq!(Ordering::Equal, role_a.cmp(&role_a));
163    /// assert_eq!(Ordering::Equal, role_b.cmp(&role_b));
164    /// ```
165    fn cmp(&self, other: &Self) -> Ordering {
166        self.position
167            .cmp(&other.position)
168            .then(other.id.get().cmp(&self.id.get()))
169    }
170}
171
172impl PartialOrd for Role {
173    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
174        Some(self.cmp(other))
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::{Permissions, Role, RoleColors};
181    use crate::{guild::RoleFlags, id::Id};
182    use serde::{Deserialize, Serialize};
183    use serde_test::Token;
184    use static_assertions::{assert_fields, assert_impl_all};
185    use std::{fmt::Debug, hash::Hash};
186    assert_fields!(
187        Role:
188        colors,
189        hoist,
190        icon,
191        id,
192        managed,
193        mentionable,
194        name,
195        permissions,
196        position,
197        tags,
198        unicode_emoji
199    );
200
201    assert_impl_all!(
202        Role: Clone,
203        Debug,
204        Deserialize<'static>,
205        Eq,
206        Hash,
207        PartialEq,
208        Serialize
209    );
210
211    #[test]
212    fn role() {
213        let role = Role {
214            #[allow(deprecated)]
215            color: 0,
216            colors: RoleColors {
217                primary_color: 0,
218                secondary_color: None,
219                tertiary_color: None,
220            },
221            hoist: true,
222            icon: None,
223            id: Id::new(123),
224            managed: false,
225            mentionable: true,
226            name: "test".to_owned(),
227            permissions: Permissions::ADMINISTRATOR,
228            position: 12,
229            flags: RoleFlags::IN_PROMPT,
230            tags: None,
231            unicode_emoji: None,
232        };
233
234        serde_test::assert_tokens(
235            &role,
236            &[
237                Token::Struct {
238                    name: "Role",
239                    len: 10,
240                },
241                Token::Str("color"),
242                Token::U32(0),
243                Token::Str("colors"),
244                Token::Struct {
245                    name: "RoleColors",
246                    len: 3,
247                },
248                Token::Str("primary_color"),
249                Token::U32(0),
250                Token::Str("secondary_color"),
251                Token::None,
252                Token::Str("tertiary_color"),
253                Token::None,
254                Token::StructEnd,
255                Token::Str("hoist"),
256                Token::Bool(true),
257                Token::Str("id"),
258                Token::NewtypeStruct { name: "Id" },
259                Token::Str("123"),
260                Token::Str("managed"),
261                Token::Bool(false),
262                Token::Str("mentionable"),
263                Token::Bool(true),
264                Token::Str("name"),
265                Token::Str("test"),
266                Token::Str("permissions"),
267                Token::Str("8"),
268                Token::Str("position"),
269                Token::I64(12),
270                Token::Str("flags"),
271                Token::U64(1),
272                Token::StructEnd,
273            ],
274        );
275    }
276}