1use crate::{
2 channel::thread::member::{ThreadMember, ThreadMemberIntermediary},
3 id::{
4 marker::{ChannelMarker, GuildMarker, UserMarker},
5 Id,
6 },
7};
8use serde::{
9 de::{value::MapAccessDeserializer, MapAccess, Visitor},
10 Deserialize, Deserializer, Serialize,
11};
12use std::fmt::{Formatter, Result as FmtResult};
13
14#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
15pub struct ThreadMembersUpdate {
16 #[serde(default)]
22 pub added_members: Vec<ThreadMember>,
23 pub guild_id: Id<GuildMarker>,
24 pub id: Id<ChannelMarker>,
25 pub member_count: i32,
29 #[serde(default)]
30 pub removed_member_ids: Vec<Id<UserMarker>>,
31}
32
33impl<'de> Deserialize<'de> for ThreadMembersUpdate {
34 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
35 deserializer.deserialize_map(ThreadMembersUpdateVisitor)
36 }
37}
38
39#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
40struct ThreadMembersUpdateIntermediary {
41 #[serde(default)]
43 pub added_members: Vec<ThreadMemberIntermediary>,
44 pub guild_id: Id<GuildMarker>,
45 pub id: Id<ChannelMarker>,
46 pub member_count: i32,
47 #[serde(default)]
48 pub removed_member_ids: Vec<Id<UserMarker>>,
49}
50
51impl ThreadMembersUpdateIntermediary {
52 fn into_thread_members_update(self) -> ThreadMembersUpdate {
53 let guild_id = self.guild_id;
54 let added_members = self
55 .added_members
56 .into_iter()
57 .map(|tm| tm.into_thread_member(guild_id))
58 .collect();
59
60 ThreadMembersUpdate {
61 added_members,
62 guild_id,
63 id: self.id,
64 member_count: self.member_count,
65 removed_member_ids: self.removed_member_ids,
66 }
67 }
68}
69
70struct ThreadMembersUpdateVisitor;
71
72impl<'de> Visitor<'de> for ThreadMembersUpdateVisitor {
73 type Value = ThreadMembersUpdate;
74
75 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
76 f.write_str("struct ThreadMembersUpdate")
77 }
78
79 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
80 let deser = MapAccessDeserializer::new(map);
81 let update = ThreadMembersUpdateIntermediary::deserialize(deser)?;
82
83 Ok(update.into_thread_members_update())
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::ThreadMembersUpdate;
90 use crate::{
91 channel::thread::ThreadMember,
92 gateway::presence::{
93 Activity, ActivityEmoji, ActivityType, ClientStatus, Presence, Status, UserOrId,
94 },
95 guild::{Member, MemberFlags},
96 id::Id,
97 test::image_hash,
98 user::User,
99 util::Timestamp,
100 };
101 use serde_test::Token;
102 use std::str::FromStr;
103
104 #[allow(clippy::too_many_lines)]
105 #[test]
106 fn thread_members_update() {
107 const JOIN_TIMESTAMP: &str = "2015-04-26T06:26:56.936000+00:00";
108 const PREMIUM_SINCE: &str = "2021-03-16T14:29:19.046000+00:00";
109
110 let joined_at = Some(Timestamp::from_str(JOIN_TIMESTAMP).expect("timestamp error"));
111 let premium_since = Timestamp::from_str(PREMIUM_SINCE).expect("timestamp error");
112 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
113
114 let member = Member {
115 avatar: Some(image_hash::AVATAR),
116 communication_disabled_until: None,
117 deaf: false,
118 flags,
119 joined_at,
120 mute: true,
121 nick: Some("twilight".to_owned()),
122 pending: false,
123 premium_since: Some(premium_since),
124 roles: Vec::new(),
125 user: User {
126 accent_color: None,
127 avatar: None,
128 avatar_decoration: None,
129 avatar_decoration_data: None,
130 banner: None,
131 bot: false,
132 discriminator: 1,
133 email: None,
134 flags: None,
135 global_name: Some("test".to_owned()),
136 id: Id::new(3),
137 locale: None,
138 mfa_enabled: None,
139 name: "twilight".to_owned(),
140 premium_type: None,
141 public_flags: None,
142 system: None,
143 verified: None,
144 },
145 };
146
147 let activity = Activity {
148 application_id: None,
149 assets: None,
150 buttons: Vec::new(),
151 created_at: Some(1_571_048_061_237),
152 details: None,
153 flags: None,
154 id: Some("aaaaaaaaaaaaaaaa".to_owned()),
155 instance: None,
156 kind: ActivityType::Custom,
157 name: "foo".to_owned(),
158 emoji: Some(ActivityEmoji {
159 name: "Test".to_string(),
160 id: None,
161 animated: None,
162 }),
163 party: None,
164 secrets: None,
165 state: None,
166 timestamps: None,
167 url: None,
168 };
169 let presence = Presence {
170 activities: vec![activity],
171 client_status: ClientStatus {
172 desktop: Some(Status::Online),
173 mobile: None,
174 web: None,
175 },
176 guild_id: Id::new(2),
177 status: Status::Online,
178 user: UserOrId::UserId { id: Id::new(3) },
179 };
180
181 let join_timestamp = Timestamp::from_str(JOIN_TIMESTAMP).expect("timestamp error");
182
183 let value = ThreadMembersUpdate {
184 added_members: vec![ThreadMember {
185 flags: 1,
186 id: Some(Id::new(123)),
187 join_timestamp,
188 member: Some(member),
189 presence: Some(presence),
190 user_id: Some(Id::new(3)),
191 }],
192 guild_id: Id::new(2),
193 id: Id::new(4),
194 member_count: 8,
195 removed_member_ids: vec![],
196 };
197
198 serde_test::assert_de_tokens(
199 &value,
200 &[
201 Token::Struct {
202 name: "ThreadMemberUpdate",
203 len: 6,
204 },
205 Token::Str("added_members"),
206 Token::Seq { len: Some(1) },
207 Token::Struct {
208 name: "ThreadMemberIntermediary",
209 len: 6,
210 },
211 Token::Str("flags"),
212 Token::U64(1),
213 Token::Str("id"),
214 Token::Some,
215 Token::NewtypeStruct { name: "ChannelId" },
216 Token::Str("123"),
217 Token::Str("join_timestamp"),
218 Token::Str(JOIN_TIMESTAMP),
219 Token::Str("member"),
220 Token::Some,
221 Token::Struct {
222 name: "Member",
223 len: 12,
224 },
225 Token::Str("avatar"),
226 Token::Some,
227 Token::Str(image_hash::AVATAR_INPUT),
228 Token::Str("communication_disabled_until"),
229 Token::None,
230 Token::Str("deaf"),
231 Token::Bool(false),
232 Token::Str("flags"),
233 Token::U64(flags.bits()),
234 Token::Str("guild_id"),
235 Token::NewtypeStruct { name: "GuildId" },
236 Token::Str("1"),
237 Token::Str("joined_at"),
238 Token::Some,
239 Token::Str(JOIN_TIMESTAMP),
240 Token::Str("mute"),
241 Token::Bool(true),
242 Token::Str("nick"),
243 Token::Some,
244 Token::Str("twilight"),
245 Token::Str("pending"),
246 Token::Bool(false),
247 Token::Str("premium_since"),
248 Token::Some,
249 Token::Str(PREMIUM_SINCE),
250 Token::Str("roles"),
251 Token::Seq { len: Some(0) },
252 Token::SeqEnd,
253 Token::Str("user"),
254 Token::Struct {
255 name: "User",
256 len: 9,
257 },
258 Token::Str("accent_color"),
259 Token::None,
260 Token::Str("avatar"),
261 Token::None,
262 Token::Str("avatar_decoration"),
263 Token::None,
264 Token::Str("avatar_decoration_data"),
265 Token::None,
266 Token::Str("banner"),
267 Token::None,
268 Token::Str("bot"),
269 Token::Bool(false),
270 Token::Str("discriminator"),
271 Token::Str("0001"),
272 Token::Str("global_name"),
273 Token::Some,
274 Token::Str("test"),
275 Token::Str("id"),
276 Token::NewtypeStruct { name: "UserId" },
277 Token::Str("3"),
278 Token::Str("username"),
279 Token::Str("twilight"),
280 Token::StructEnd,
281 Token::StructEnd,
282 Token::Str("presence"),
283 Token::Some,
284 Token::Struct {
285 name: "PresenceIntermediary",
286 len: 5,
287 },
288 Token::Str("activities"),
289 Token::Seq { len: Some(1) },
290 Token::Struct {
291 name: "Activity",
292 len: 5,
293 },
294 Token::Str("created_at"),
295 Token::Some,
296 Token::U64(1_571_048_061_237),
297 Token::Str("emoji"),
298 Token::Some,
299 Token::Struct {
300 name: "ActivityEmoji",
301 len: 1,
302 },
303 Token::Str("name"),
304 Token::Str("Test"),
305 Token::StructEnd,
306 Token::Str("id"),
307 Token::Some,
308 Token::Str("aaaaaaaaaaaaaaaa"),
309 Token::Str("type"),
310 Token::U8(4),
311 Token::Str("name"),
312 Token::Str("foo"),
313 Token::StructEnd,
314 Token::SeqEnd,
315 Token::Str("client_status"),
316 Token::Struct {
317 name: "ClientStatus",
318 len: 1,
319 },
320 Token::Str("desktop"),
321 Token::Some,
322 Token::Enum { name: "Status" },
323 Token::Str("online"),
324 Token::Unit,
325 Token::StructEnd,
326 Token::Str("guild_id"),
327 Token::Some,
328 Token::NewtypeStruct { name: "GuildId" },
329 Token::Str("2"),
330 Token::Str("status"),
331 Token::Enum { name: "Status" },
332 Token::Str("online"),
333 Token::Unit,
334 Token::Str("user"),
335 Token::Struct {
336 name: "UserOrId",
337 len: 1,
338 },
339 Token::Str("id"),
340 Token::NewtypeStruct { name: "UserId" },
341 Token::Str("3"),
342 Token::StructEnd,
343 Token::StructEnd,
344 Token::Str("user_id"),
345 Token::Some,
346 Token::NewtypeStruct { name: "UserId" },
347 Token::Str("3"),
348 Token::StructEnd,
349 Token::SeqEnd,
350 Token::Str("guild_id"),
351 Token::NewtypeStruct { name: "GuildId" },
352 Token::Str("2"),
353 Token::Str("id"),
354 Token::NewtypeStruct { name: "ChannelId" },
355 Token::Str("4"),
356 Token::Str("member_count"),
357 Token::I32(8),
358 Token::Str("removed_member_ids"),
359 Token::Seq { len: Some(0) },
360 Token::SeqEnd,
361 Token::StructEnd,
362 ],
363 );
364 }
365}