twilight_model/guild/
member.rs

1//! Mostly internal custom serde deserializers.
2
3use super::MemberFlags;
4use crate::{
5    id::{Id, marker::RoleMarker},
6    user::User,
7    util::{ImageHash, Timestamp},
8};
9
10use crate::user::AvatarDecorationData;
11use serde::{Deserialize, Serialize};
12
13/// [`User`] that is in a [`Guild`].
14///
15/// [`Guild`]: super::Guild
16#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
17pub struct Member {
18    /// Member's guild avatar.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub avatar: Option<ImageHash>,
21    /// Member's avatar decoration data.
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub avatar_decoration_data: Option<AvatarDecorationData>,
24    /// Member's guild banner.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub banner: Option<ImageHash>,
27    pub communication_disabled_until: Option<Timestamp>,
28    pub deaf: bool,
29    /// Flags for the member.
30    ///
31    /// Defaults to an empty bitfield.
32    pub flags: MemberFlags,
33    pub joined_at: Option<Timestamp>,
34    pub mute: bool,
35    pub nick: Option<String>,
36    /// Whether the user has yet to pass the guild's [Membership Screening]
37    /// requirements.
38    #[serde(default)]
39    pub pending: bool,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub premium_since: Option<Timestamp>,
42    pub roles: Vec<Id<RoleMarker>>,
43    pub user: User,
44}
45
46#[cfg(test)]
47mod tests {
48    use super::Member;
49    use crate::{
50        guild::MemberFlags,
51        id::Id,
52        test::image_hash,
53        user::User,
54        util::datetime::{Timestamp, TimestampParseError},
55    };
56    use serde_test::Token;
57    use std::str::FromStr;
58
59    #[test]
60    #[allow(clippy::too_many_lines)]
61    fn member_deserializer() -> Result<(), TimestampParseError> {
62        let joined_at = Some(Timestamp::from_str("2015-04-26T06:26:56.936000+00:00")?);
63        let premium_since = Timestamp::from_str("2021-03-16T14:29:19.046000+00:00")?;
64        let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
65
66        let value = Member {
67            avatar: Some(image_hash::AVATAR),
68            avatar_decoration_data: None,
69            banner: None,
70            communication_disabled_until: None,
71            deaf: false,
72            flags,
73            joined_at,
74            mute: true,
75            nick: Some("twilight".to_owned()),
76            pending: false,
77            premium_since: Some(premium_since),
78            roles: Vec::new(),
79            user: User {
80                accent_color: None,
81                avatar: None,
82                avatar_decoration: None,
83                avatar_decoration_data: None,
84                banner: None,
85                bot: false,
86                discriminator: 1,
87                email: None,
88                flags: None,
89                global_name: Some("test".to_owned()),
90                id: Id::new(3),
91                locale: None,
92                mfa_enabled: None,
93                name: "twilight".to_owned(),
94                premium_type: None,
95                primary_guild: None,
96                public_flags: None,
97                system: None,
98                verified: None,
99            },
100        };
101
102        serde_test::assert_tokens(
103            &value,
104            &[
105                Token::Struct {
106                    name: "Member",
107                    len: 11,
108                },
109                Token::Str("avatar"),
110                Token::Some,
111                Token::Str(image_hash::AVATAR_INPUT),
112                Token::Str("communication_disabled_until"),
113                Token::None,
114                Token::Str("deaf"),
115                Token::Bool(false),
116                Token::Str("flags"),
117                Token::U64(flags.bits()),
118                Token::Str("joined_at"),
119                Token::Some,
120                Token::Str("2015-04-26T06:26:56.936000+00:00"),
121                Token::Str("mute"),
122                Token::Bool(true),
123                Token::Str("nick"),
124                Token::Some,
125                Token::Str("twilight"),
126                Token::Str("pending"),
127                Token::Bool(false),
128                Token::Str("premium_since"),
129                Token::Some,
130                Token::Str("2021-03-16T14:29:19.046000+00:00"),
131                Token::Str("roles"),
132                Token::Seq { len: Some(0) },
133                Token::SeqEnd,
134                Token::Str("user"),
135                Token::Struct {
136                    name: "User",
137                    len: 10,
138                },
139                Token::Str("accent_color"),
140                Token::None,
141                Token::Str("avatar"),
142                Token::None,
143                Token::Str("avatar_decoration"),
144                Token::None,
145                Token::Str("avatar_decoration_data"),
146                Token::None,
147                Token::Str("banner"),
148                Token::None,
149                Token::Str("bot"),
150                Token::Bool(false),
151                Token::Str("discriminator"),
152                Token::Str("0001"),
153                Token::Str("global_name"),
154                Token::Some,
155                Token::Str("test"),
156                Token::Str("id"),
157                Token::NewtypeStruct { name: "Id" },
158                Token::Str("3"),
159                Token::Str("username"),
160                Token::Str("twilight"),
161                Token::StructEnd,
162                Token::StructEnd,
163            ],
164        );
165
166        Ok(())
167    }
168
169    #[test]
170    #[allow(clippy::too_many_lines)]
171    fn guild_member_communication_disabled_until() -> Result<(), TimestampParseError> {
172        let communication_disabled_until = Timestamp::from_str("2021-12-23T14:29:19.046000+00:00")?;
173        let joined_at = Some(Timestamp::from_str("2015-04-26T06:26:56.936000+00:00")?);
174        let premium_since = Timestamp::from_str("2021-03-16T14:29:19.046000+00:00")?;
175        let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
176
177        let value = Member {
178            avatar: Some(image_hash::AVATAR),
179            avatar_decoration_data: None,
180            banner: None,
181            communication_disabled_until: Some(communication_disabled_until),
182            deaf: false,
183            flags,
184            joined_at,
185            mute: true,
186            nick: Some("twilight".to_owned()),
187            pending: false,
188            premium_since: Some(premium_since),
189            roles: Vec::new(),
190            user: User {
191                accent_color: None,
192                avatar: None,
193                avatar_decoration: None,
194                avatar_decoration_data: None,
195                banner: None,
196                bot: false,
197                discriminator: 1,
198                email: None,
199                flags: None,
200                global_name: Some("test".to_owned()),
201                id: Id::new(3),
202                locale: None,
203                mfa_enabled: None,
204                name: "twilight".to_owned(),
205                premium_type: None,
206                primary_guild: None,
207                public_flags: None,
208                system: None,
209                verified: None,
210            },
211        };
212
213        serde_test::assert_tokens(
214            &value,
215            &[
216                Token::Struct {
217                    name: "Member",
218                    len: 11,
219                },
220                Token::Str("avatar"),
221                Token::Some,
222                Token::Str(image_hash::AVATAR_INPUT),
223                Token::Str("communication_disabled_until"),
224                Token::Some,
225                Token::Str("2021-12-23T14:29:19.046000+00:00"),
226                Token::Str("deaf"),
227                Token::Bool(false),
228                Token::Str("flags"),
229                Token::U64(flags.bits()),
230                Token::Str("joined_at"),
231                Token::Some,
232                Token::Str("2015-04-26T06:26:56.936000+00:00"),
233                Token::Str("mute"),
234                Token::Bool(true),
235                Token::Str("nick"),
236                Token::Some,
237                Token::Str("twilight"),
238                Token::Str("pending"),
239                Token::Bool(false),
240                Token::Str("premium_since"),
241                Token::Some,
242                Token::Str("2021-03-16T14:29:19.046000+00:00"),
243                Token::Str("roles"),
244                Token::Seq { len: Some(0) },
245                Token::SeqEnd,
246                Token::Str("user"),
247                Token::Struct {
248                    name: "User",
249                    len: 10,
250                },
251                Token::Str("accent_color"),
252                Token::None,
253                Token::Str("avatar"),
254                Token::None,
255                Token::Str("avatar_decoration"),
256                Token::None,
257                Token::Str("avatar_decoration_data"),
258                Token::None,
259                Token::Str("banner"),
260                Token::None,
261                Token::Str("bot"),
262                Token::Bool(false),
263                Token::Str("discriminator"),
264                Token::Str("0001"),
265                Token::Str("global_name"),
266                Token::Some,
267                Token::Str("test"),
268                Token::Str("id"),
269                Token::NewtypeStruct { name: "Id" },
270                Token::Str("3"),
271                Token::Str("username"),
272                Token::Str("twilight"),
273                Token::StructEnd,
274                Token::StructEnd,
275            ],
276        );
277
278        Ok(())
279    }
280}