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