1mod avatar_decoration_data;
2mod connection;
3mod connection_visibility;
4mod current_user;
5mod current_user_guild;
6mod flags;
7mod premium_type;
8
9pub use self::{
10 avatar_decoration_data::AvatarDecorationData, connection::Connection,
11 connection_visibility::ConnectionVisibility, current_user::CurrentUser,
12 current_user_guild::CurrentUserGuild, flags::UserFlags, premium_type::PremiumType,
13};
14
15use crate::{
16 id::{marker::UserMarker, Id},
17 util::image_hash::ImageHash,
18};
19use serde::{Deserialize, Serialize};
20use std::fmt::{Display, Formatter, Result as FmtResult};
21
22pub(crate) mod discriminator {
23 use super::DiscriminatorDisplay;
24 use serde::{
25 de::{Deserializer, Error as DeError, Visitor},
26 ser::Serializer,
27 };
28 use std::fmt::{Formatter, Result as FmtResult};
29
30 struct DiscriminatorVisitor;
31
32 impl Visitor<'_> for DiscriminatorVisitor {
33 type Value = u16;
34
35 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
36 f.write_str("string or integer discriminator")
37 }
38
39 fn visit_u64<E: DeError>(self, value: u64) -> Result<Self::Value, E> {
40 value.try_into().map_err(DeError::custom)
41 }
42
43 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
44 value.parse().map_err(DeError::custom)
45 }
46 }
47
48 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u16, D::Error> {
49 deserializer.deserialize_any(DiscriminatorVisitor)
50 }
51
52 #[allow(clippy::trivially_copy_pass_by_ref)]
54 pub fn serialize<S: Serializer>(value: &u16, serializer: S) -> Result<S::Ok, S::Error> {
55 serializer.collect_str(&DiscriminatorDisplay(*value))
56 }
57}
58
59#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
78#[must_use = "display implementations should be formatted"]
79pub struct DiscriminatorDisplay(u16);
80
81impl DiscriminatorDisplay {
82 pub const fn new(discriminator: u16) -> Self {
95 Self(discriminator)
96 }
97
98 pub const fn get(self) -> u16 {
100 self.0
101 }
102}
103
104impl Display for DiscriminatorDisplay {
105 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
106 match self.0 {
111 1..=9 => f.write_str("000")?,
112 10..=99 => f.write_str("00")?,
113 100..=999 => f.write_str("0")?,
114 _ => {}
115 }
116
117 Display::fmt(&self.0, f)
118 }
119}
120
121#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
122pub struct User {
123 pub accent_color: Option<u32>,
127 pub avatar: Option<ImageHash>,
128 pub avatar_decoration: Option<ImageHash>,
130 pub avatar_decoration_data: Option<AvatarDecorationData>,
132 pub banner: Option<ImageHash>,
134 #[serde(default)]
135 pub bot: bool,
136 #[serde(with = "discriminator")]
156 pub discriminator: u16,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub email: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub flags: Option<UserFlags>,
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub global_name: Option<String>,
164 pub id: Id<UserMarker>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub locale: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub mfa_enabled: Option<bool>,
169 #[serde(rename = "username")]
170 pub name: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub premium_type: Option<PremiumType>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub public_flags: Option<UserFlags>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub system: Option<bool>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub verified: Option<bool>,
179}
180
181impl User {
182 pub const fn discriminator(&self) -> DiscriminatorDisplay {
187 DiscriminatorDisplay::new(self.discriminator)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{DiscriminatorDisplay, PremiumType, User, UserFlags};
194 use crate::{id::Id, test::image_hash};
195 use serde_test::Token;
196 use static_assertions::assert_impl_all;
197 use std::{fmt::Debug, hash::Hash};
198
199 assert_impl_all!(
200 DiscriminatorDisplay: Clone,
201 Copy,
202 Debug,
203 Eq,
204 Hash,
205 PartialEq,
206 Send,
207 Sync
208 );
209
210 fn user_tokens(discriminator_token: Token) -> Vec<Token> {
211 vec![
212 Token::Struct {
213 name: "User",
214 len: 17,
215 },
216 Token::Str("accent_color"),
217 Token::None,
218 Token::Str("avatar"),
219 Token::Some,
220 Token::Str(image_hash::AVATAR_INPUT),
221 Token::Str("avatar_decoration"),
222 Token::Some,
223 Token::Str(image_hash::AVATAR_DECORATION_INPUT),
224 Token::Str("avatar_decoration_data"),
225 Token::None,
226 Token::Str("banner"),
227 Token::Some,
228 Token::Str(image_hash::BANNER_INPUT),
229 Token::Str("bot"),
230 Token::Bool(false),
231 Token::Str("discriminator"),
232 discriminator_token,
233 Token::Str("email"),
234 Token::Some,
235 Token::Str("address@example.com"),
236 Token::Str("flags"),
237 Token::Some,
238 Token::U64(131_584),
239 Token::Str("global_name"),
240 Token::Some,
241 Token::Str("test"),
242 Token::Str("id"),
243 Token::NewtypeStruct { name: "Id" },
244 Token::Str("1"),
245 Token::Str("locale"),
246 Token::Some,
247 Token::Str("en-us"),
248 Token::Str("mfa_enabled"),
249 Token::Some,
250 Token::Bool(true),
251 Token::Str("username"),
252 Token::Str("test"),
253 Token::Str("premium_type"),
254 Token::Some,
255 Token::U8(2),
256 Token::Str("public_flags"),
257 Token::Some,
258 Token::U64(131_584),
259 Token::Str("verified"),
260 Token::Some,
261 Token::Bool(true),
262 Token::StructEnd,
263 ]
264 }
265
266 fn user_tokens_complete(discriminator_token: Token) -> Vec<Token> {
267 vec![
268 Token::Struct {
269 name: "User",
270 len: 18,
271 },
272 Token::Str("accent_color"),
273 Token::None,
274 Token::Str("avatar"),
275 Token::Some,
276 Token::Str(image_hash::AVATAR_INPUT),
277 Token::Str("avatar_decoration"),
278 Token::Some,
279 Token::Str(image_hash::AVATAR_DECORATION_INPUT),
280 Token::Str("avatar_decoration_data"),
281 Token::None,
282 Token::Str("banner"),
283 Token::Some,
284 Token::Str(image_hash::BANNER_INPUT),
285 Token::Str("bot"),
286 Token::Bool(false),
287 Token::Str("discriminator"),
288 discriminator_token,
289 Token::Str("email"),
290 Token::Some,
291 Token::Str("address@example.com"),
292 Token::Str("flags"),
293 Token::Some,
294 Token::U64(131_584),
295 Token::Str("global_name"),
296 Token::Some,
297 Token::Str("test"),
298 Token::Str("id"),
299 Token::NewtypeStruct { name: "Id" },
300 Token::Str("1"),
301 Token::Str("locale"),
302 Token::Some,
303 Token::Str("en-us"),
304 Token::Str("mfa_enabled"),
305 Token::Some,
306 Token::Bool(true),
307 Token::Str("username"),
308 Token::Str("test"),
309 Token::Str("premium_type"),
310 Token::Some,
311 Token::U8(2),
312 Token::Str("public_flags"),
313 Token::Some,
314 Token::U64(131_584),
315 Token::Str("system"),
316 Token::Some,
317 Token::Bool(true),
318 Token::Str("verified"),
319 Token::Some,
320 Token::Bool(true),
321 Token::StructEnd,
322 ]
323 }
324
325 #[test]
326 fn discriminator_display() {
327 assert_eq!(3030, DiscriminatorDisplay::new(3030).get());
328 assert_eq!("0003", DiscriminatorDisplay::new(3).to_string());
329 assert_eq!("0033", DiscriminatorDisplay::new(33).to_string());
330 assert_eq!("0333", DiscriminatorDisplay::new(333).to_string());
331 assert_eq!("3333", DiscriminatorDisplay::new(3333).to_string());
332 assert_eq!("0", DiscriminatorDisplay::new(0).to_string());
333 }
334
335 #[test]
336 fn user() {
337 let value = User {
338 accent_color: None,
339 avatar: Some(image_hash::AVATAR),
340 avatar_decoration: Some(image_hash::AVATAR_DECORATION),
341 avatar_decoration_data: None,
342 banner: Some(image_hash::BANNER),
343 bot: false,
344 discriminator: 1,
345 email: Some("address@example.com".to_owned()),
346 flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
347 global_name: Some("test".to_owned()),
348 id: Id::new(1),
349 locale: Some("en-us".to_owned()),
350 mfa_enabled: Some(true),
351 name: "test".to_owned(),
352 premium_type: Some(PremiumType::Nitro),
353 public_flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
354 system: None,
355 verified: Some(true),
356 };
357
358 serde_test::assert_tokens(&value, &user_tokens(Token::Str("0001")));
361
362 serde_test::assert_de_tokens(&value, &user_tokens(Token::U64(1)));
366 }
367
368 #[test]
369 fn user_no_discriminator() {
370 let value = User {
371 accent_color: None,
372 avatar: Some(image_hash::AVATAR),
373 avatar_decoration: Some(image_hash::AVATAR_DECORATION),
374 avatar_decoration_data: None,
375 banner: Some(image_hash::BANNER),
376 bot: false,
377 discriminator: 0,
378 email: Some("address@example.com".to_owned()),
379 flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
380 global_name: Some("test".to_owned()),
381 id: Id::new(1),
382 locale: Some("en-us".to_owned()),
383 mfa_enabled: Some(true),
384 name: "test".to_owned(),
385 premium_type: Some(PremiumType::Nitro),
386 public_flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
387 system: None,
388 verified: Some(true),
389 };
390
391 serde_test::assert_tokens(&value, &user_tokens(Token::Str("0")));
395 serde_test::assert_de_tokens(&value, &user_tokens(Token::U64(0)));
396 }
397
398 #[test]
399 fn user_complete() {
400 let value = User {
401 accent_color: None,
402 avatar: Some(image_hash::AVATAR),
403 avatar_decoration: Some(image_hash::AVATAR_DECORATION),
404 avatar_decoration_data: None,
405 banner: Some(image_hash::BANNER),
406 bot: false,
407 discriminator: 1,
408 email: Some("address@example.com".to_owned()),
409 flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
410 global_name: Some("test".to_owned()),
411 id: Id::new(1),
412 locale: Some("en-us".to_owned()),
413 mfa_enabled: Some(true),
414 name: "test".to_owned(),
415 premium_type: Some(PremiumType::Nitro),
416 public_flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER),
417 system: Some(true),
418 verified: Some(true),
419 };
420
421 serde_test::assert_tokens(&value, &user_tokens_complete(Token::Str("0001")));
424
425 serde_test::assert_de_tokens(&value, &user_tokens_complete(Token::U64(1)));
429 }
430}