1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
use serde::{Deserialize, de::Visitor};
use crate::{
Mentionable,
types::{Permissions, Snowflake},
};
/// [Role Object](https://discord.com/developers/docs/topics/permissions#role-object)
#[derive(Debug, Clone, Deserialize)]
pub struct Role {
/// role id
pub id: Snowflake,
/// role name
pub name: String,
/// integer representation of hexadecimal color code
pub color: i32,
/// if this role is pinned in the user listing
pub hoist: bool,
/// role [icon hash](https://discord.com/developers/docs/reference#image-formatting)
pub icon: Option<String>,
/// role unicode emoji
pub unicode_emoji: Option<String>,
/// position of this role
pub position: i32,
/// permission bit set
pub permissions: Permissions,
/// whether this role is managed by an integration
pub managed: bool,
/// whether this role is mentionable
pub mentionable: bool,
/// the tags this role has
pub tags: Option<RoleTags>,
}
impl Mentionable for Role {
fn to_mention(&self) -> String {
format!("<@&{}>", self.id)
}
}
/// [Role Subscription Data Object](https://discord.com/developers/docs/resources/channel#role-subscription-data-object)
#[derive(Debug, Clone, Deserialize)]
pub struct RoleSubscriptionData {
/// the id of the sku and listing that the user is subscribed to
pub role_subscription_listing_id: Snowflake,
/// the name of the tier that the user is subscribed to
pub tier_name: String,
/// the cumulative number of months that the user has been subscribed for
pub total_months_subscribed: i32,
/// whether this notification is for a renewal rather than a new purchase
pub is_renewal: bool,
}
/// [Role Tags Structure](https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure)
#[derive(Debug, Clone)]
pub struct RoleTags {
/// the id of the bot this role belongs to
pub bot_id: Option<Snowflake>,
/// the id of the integration this role belongs to
pub integration_id: Option<Snowflake>,
/// whether this is the guild's Booster role
pub premium_subscriber: bool,
/// the id of this role's subscription sku and listing
pub subscription_listing_id: Option<Snowflake>,
/// whether this role is available for purchase
pub available_for_purchase: bool,
/// whether this role is a guild's linked role
pub guild_connections: bool,
}
impl<'de> Deserialize<'de> for RoleTags {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"bot_id",
"integration_id",
"premium_subscriber",
"subscription_listing_id",
"available_for_purchase",
"guild_connections",
];
enum Field {
BotId,
IntegrationId,
PremiumSubscriber,
SubscriptionListingId,
AvailableForPurchase,
GuildConnections,
}
// This part could also be generated independently by:
//
// #[derive(Deserialize)]
// #[serde(field_identifier, rename_all = "lowercase")]
// enum Field { Secs, Nanos }
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a field from RoleTags")
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: serde::de::Error,
{
match value {
"bot_id" => Ok(Field::BotId),
"integration_id" => Ok(Field::IntegrationId),
"premium_subscriber" => Ok(Field::PremiumSubscriber),
"subscription_listing_id" => Ok(Field::SubscriptionListingId),
"available_for_purchase" => Ok(Field::AvailableForPurchase),
"guild_connections" => Ok(Field::GuildConnections),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct RoleTagsVisitor;
impl<'de> Visitor<'de> for RoleTagsVisitor {
type Value = RoleTags;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct RoleTags")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut bot_id: Option<Snowflake> = None;
let mut integration_id: Option<Snowflake> = None;
let mut premium_subscriber: bool = false;
let mut subscription_listing_id: Option<Snowflake> = None;
let mut available_for_purchase: bool = false;
let mut guild_connections: bool = false;
while let Some(key) = map.next_key()? {
match key {
Field::AvailableForPurchase => {
map.next_value::<serde::de::IgnoredAny>()?;
available_for_purchase = true;
}
Field::BotId => {
bot_id = map.next_value()?;
}
Field::IntegrationId => {
integration_id = map.next_value()?;
}
Field::PremiumSubscriber => {
map.next_value::<serde::de::IgnoredAny>()?;
premium_subscriber = true;
}
Field::SubscriptionListingId => {
subscription_listing_id = map.next_value()?;
}
Field::GuildConnections => {
map.next_value::<serde::de::IgnoredAny>()?;
guild_connections = true;
}
}
}
Ok(RoleTags {
bot_id,
integration_id,
premium_subscriber,
subscription_listing_id,
available_for_purchase,
guild_connections,
})
}
}
deserializer.deserialize_struct("RoleTags", FIELDS, RoleTagsVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn role_tags_deserializes_all_true() {
let json = r#"{
"bot_id": "282265607313817601",
"integration_id": "282265607313817601",
"subscription_listing_id": "282265607313817601",
"premium_subscriber": null,
"available_for_purchase": null,
"guild_connections": null
}"#;
let result = serde_json::from_str::<RoleTags>(json);
assert!(result.is_ok());
let role_tags = result.unwrap();
assert!(role_tags.premium_subscriber);
assert!(role_tags.available_for_purchase);
assert!(role_tags.guild_connections);
}
#[test]
pub fn role_tags_deserializes_all_false() {
let json = r#"{
"bot_id": "282265607313817601",
"integration_id": "282265607313817601",
"subscription_listing_id": "282265607313817601"
}"#;
let result = serde_json::from_str::<RoleTags>(json);
assert!(result.is_ok());
let role_tags = result.unwrap();
assert!(!role_tags.premium_subscriber);
assert!(!role_tags.available_for_purchase);
assert!(!role_tags.guild_connections);
}
}