1pub mod activity_button;
2
3mod activity;
4mod activity_assets;
5mod activity_emoji;
6mod activity_flags;
7mod activity_party;
8mod activity_secrets;
9mod activity_timestamps;
10mod activity_type;
11mod client_status;
12mod minimal_activity;
13mod status;
14
15pub use self::{
16 activity::Activity, activity_assets::ActivityAssets, activity_button::ActivityButton,
17 activity_emoji::ActivityEmoji, activity_flags::ActivityFlags, activity_party::ActivityParty,
18 activity_secrets::ActivitySecrets, activity_timestamps::ActivityTimestamps,
19 activity_type::ActivityType, client_status::ClientStatus, minimal_activity::MinimalActivity,
20 status::Status,
21};
22
23use crate::{
24 id::{
25 marker::{GuildMarker, UserMarker},
26 Id,
27 },
28 user::User,
29};
30use serde::{
31 de::{
32 value::MapAccessDeserializer, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor,
33 },
34 Deserialize, Serialize,
35};
36use std::fmt::{Formatter, Result as FmtResult};
37
38#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
39pub struct Presence {
40 #[serde(default)]
41 pub activities: Vec<Activity>,
42 pub client_status: ClientStatus,
43 pub guild_id: Id<GuildMarker>,
44 pub status: Status,
45 pub user: UserOrId,
46}
47
48#[allow(clippy::large_enum_variant)]
49#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
50#[serde(untagged)]
51pub enum UserOrId {
52 User(User),
53 UserId { id: Id<UserMarker> },
54}
55
56impl UserOrId {
57 pub const fn id(&self) -> Id<UserMarker> {
59 match self {
60 UserOrId::User(u) => u.id,
61 UserOrId::UserId { id } => *id,
62 }
63 }
64}
65
66#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
67pub(crate) struct PresenceIntermediary {
68 #[serde(default)]
69 pub activities: Vec<Activity>,
70 pub client_status: ClientStatus,
71 pub guild_id: Option<Id<GuildMarker>>,
72 pub nick: Option<String>,
73 pub status: Status,
74 pub user: UserOrId,
75}
76
77impl PresenceIntermediary {
78 pub fn into_presence(self, guild_id: Id<GuildMarker>) -> Presence {
80 Presence {
81 activities: self.activities,
82 client_status: self.client_status,
83 guild_id: self.guild_id.unwrap_or(guild_id),
84 status: self.status,
85 user: self.user,
86 }
87 }
88}
89
90struct PresenceVisitor(Id<GuildMarker>);
91
92impl<'de> Visitor<'de> for PresenceVisitor {
93 type Value = Presence;
94
95 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
96 f.write_str("Presence struct")
97 }
98
99 fn visit_map<M: MapAccess<'de>>(self, map: M) -> Result<Self::Value, M::Error> {
100 let deser = MapAccessDeserializer::new(map);
101 let presence = PresenceIntermediary::deserialize(deser)?;
102
103 Ok(Presence {
104 activities: presence.activities,
105 client_status: presence.client_status,
106 guild_id: presence.guild_id.unwrap_or(self.0),
107 status: presence.status,
108 user: presence.user,
109 })
110 }
111}
112
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub struct PresenceDeserializer(Id<GuildMarker>);
115
116impl PresenceDeserializer {
117 pub const fn new(guild_id: Id<GuildMarker>) -> Self {
120 Self(guild_id)
121 }
122}
123
124impl<'de> DeserializeSeed<'de> for PresenceDeserializer {
125 type Value = Presence;
126
127 fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
128 deserializer.deserialize_map(PresenceVisitor(self.0))
129 }
130}
131
132#[derive(Clone, Debug, Eq, PartialEq)]
133pub struct PresenceListDeserializer(Id<GuildMarker>);
134
135impl PresenceListDeserializer {
136 pub const fn new(guild_id: Id<GuildMarker>) -> Self {
139 Self(guild_id)
140 }
141}
142
143struct PresenceListDeserializerVisitor(Id<GuildMarker>);
144
145impl<'de> Visitor<'de> for PresenceListDeserializerVisitor {
146 type Value = Vec<Presence>;
147
148 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
149 f.write_str("a sequence of presences")
150 }
151
152 fn visit_seq<S: SeqAccess<'de>>(self, mut seq: S) -> Result<Self::Value, S::Error> {
153 let mut list = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
154
155 while let Some(presence) = seq.next_element_seed(PresenceDeserializer(self.0))? {
156 list.push(presence);
157 }
158
159 Ok(list)
160 }
161}
162
163impl<'de> DeserializeSeed<'de> for PresenceListDeserializer {
164 type Value = Vec<Presence>;
165
166 fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
167 deserializer.deserialize_any(PresenceListDeserializerVisitor(self.0))
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::{
174 Activity, ActivityEmoji, ActivityType, ClientStatus, Presence, PresenceListDeserializer,
175 Status, UserOrId,
176 };
177 use crate::id::Id;
178 use serde::de::DeserializeSeed;
179 use serde_json::Deserializer;
180 use serde_test::Token;
181
182 #[test]
183 #[allow(clippy::too_many_lines)]
184 fn custom() {
185 let activity = Activity {
186 application_id: None,
187 assets: None,
188 buttons: Vec::new(),
189 created_at: Some(1_571_048_061_237),
190 details: None,
191 flags: None,
192 id: Some("aaaaaaaaaaaaaaaa".to_owned()),
193 instance: None,
194 kind: ActivityType::Custom,
195 name: "foo".to_owned(),
196 emoji: Some(ActivityEmoji {
197 name: "Test".to_string(),
198 id: None,
199 animated: None,
200 }),
201 party: None,
202 secrets: None,
203 state: None,
204 timestamps: None,
205 url: None,
206 };
207 let value = Presence {
208 activities: vec![activity],
209 client_status: ClientStatus {
210 desktop: Some(Status::Online),
211 mobile: None,
212 web: None,
213 },
214 guild_id: Id::new(2),
215 status: Status::Online,
216 user: UserOrId::UserId { id: Id::new(1) },
217 };
218
219 serde_test::assert_de_tokens(
220 &value,
221 &[
222 Token::Struct {
223 name: "Presence",
224 len: 4,
225 },
226 Token::Str("user"),
227 Token::Struct {
228 name: "UserOrId",
229 len: 1,
230 },
231 Token::Str("id"),
232 Token::Str("1"),
233 Token::StructEnd,
234 Token::Str("guild_id"),
235 Token::NewtypeStruct { name: "Id" },
236 Token::Str("2"),
237 Token::Str("status"),
238 Token::Enum { name: "Status" },
239 Token::Str("online"),
240 Token::Unit,
241 Token::Str("client_status"),
242 Token::Struct {
243 name: "ClientStatus",
244 len: 3,
245 },
246 Token::Str("desktop"),
247 Token::Some,
248 Token::Enum { name: "Status" },
249 Token::Str("online"),
250 Token::Unit,
251 Token::Str("mobile"),
252 Token::None,
253 Token::Str("web"),
254 Token::None,
255 Token::StructEnd,
256 Token::Str("activities"),
257 Token::Seq { len: Some(1) },
258 Token::Struct {
259 name: "Activity",
260 len: 4,
261 },
262 Token::Str("type"),
263 Token::U8(4),
264 Token::Str("name"),
265 Token::Str("foo"),
266 Token::Str("emoji"),
267 Token::Some,
268 Token::Struct {
269 name: "ActivityEmoji",
270 len: 3,
271 },
272 Token::Str("name"),
273 Token::Str("Test"),
274 Token::Str("id"),
275 Token::None,
276 Token::Str("animated"),
277 Token::None,
278 Token::StructEnd,
279 Token::Str("id"),
280 Token::Some,
281 Token::Str("aaaaaaaaaaaaaaaa"),
282 Token::Str("created_at"),
283 Token::Some,
284 Token::U64(1_571_048_061_237),
285 Token::StructEnd,
286 Token::SeqEnd,
287 Token::StructEnd,
288 ],
289 );
290 }
291
292 #[test]
297 fn presence_map_guild_id_default() {
298 let input = r#"[{
299 "user": {
300 "id": "1"
301 },
302 "status": "online",
303 "client_status": {
304 "desktop": "online"
305 },
306 "activities": []
307 }]"#;
308
309 let expected = Vec::from([Presence {
310 activities: vec![],
311 client_status: ClientStatus {
312 desktop: Some(Status::Online),
313 mobile: None,
314 web: None,
315 },
316 guild_id: Id::new(2),
317 status: Status::Online,
318 user: UserOrId::UserId { id: Id::new(1) },
319 }]);
320
321 let mut json_deserializer = Deserializer::from_str(input);
322 let deserializer = PresenceListDeserializer::new(Id::new(2));
323 let actual = deserializer.deserialize(&mut json_deserializer).unwrap();
324
325 assert_eq!(actual, expected);
326 }
327}