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}