strife/model/gateway/
presence.rs

1//! Models related to Rich Presence.
2
3use chrono::{DateTime, FixedOffset};
4use serde::{Deserialize, Serialize};
5
6use crate::model::gateway::activity::Activity;
7use crate::model::id::{GuildId, RoleId, ToSnowflakeId, UserId};
8use crate::model::user::User;
9
10/// A user with possibly only partial information.
11#[derive(Clone, Debug, Deserialize, Serialize)]
12#[serde(untagged)]
13pub enum PartialUser {
14    /// A full user object.
15    Full(User),
16    /// A partial user object with only the `id` field.
17    #[non_exhaustive]
18    Partial {
19        /// The ID of the user.
20        id: UserId,
21    },
22}
23
24impl crate::model::id::private::Sealed for PartialUser {}
25
26impl ToSnowflakeId for PartialUser {
27    type Id = UserId;
28
29    fn id(&self) -> Self::Id {
30        match self {
31            PartialUser::Full(user) => user.id,
32            PartialUser::Partial { id } => *id,
33        }
34    }
35}
36
37/// The online status of a [`User`] in a [`Presence`].
38///
39/// [`User`]: ../../user/struct.User.html
40/// [`Presence`]: struct.Presence.html
41#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
42pub enum OnlineStatus {
43    /// Idle.
44    #[serde(rename = "idle")]
45    Idle,
46    /// Do not disturb.
47    #[serde(rename = "dnd")]
48    DoNotDisturb,
49    /// Online.
50    #[serde(rename = "online")]
51    Online,
52    /// Offline or invisible.
53    #[serde(rename = "offline")]
54    Offline,
55}
56
57impl OnlineStatus {
58    /// Used in serde `skip_serializing_if` attribute.
59    #[allow(clippy::trivially_copy_pass_by_ref)]
60    #[inline]
61    fn is_offline(&self) -> bool {
62        match self {
63            OnlineStatus::Offline => true,
64            _ => false,
65        }
66    }
67}
68
69impl Default for OnlineStatus {
70    fn default() -> Self {
71        OnlineStatus::Offline
72    }
73}
74
75/// Statuses of the active sessions for each platform for a [`User`].
76///
77/// [`User`]: ../../user/struct.User.html
78#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
79pub struct ClientStatus {
80    /// The status set for an active desktop (Windows, Linux, Mac) application
81    /// session.
82    #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")]
83    pub desktop: OnlineStatus,
84    /// The status set for an active mobile (iOS, Android) application session.
85    #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")]
86    pub mobile: OnlineStatus,
87    /// The status set for an active web (browser, bot account) application
88    /// session.
89    #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")]
90    pub web: OnlineStatus,
91}
92
93/// The presence state of a [`User`] in a [`Guild`].
94///
95/// [`User`]: ../../user/struct.User.html
96/// [`Guild`]: ../../guild/struct.Guild.html
97#[derive(Clone, Debug, Deserialize, Serialize)]
98pub struct Presence {
99    /// The user the presence relates to.
100    pub user: PartialUser,
101    /// The roles of the user.
102    pub roles: Vec<RoleId>,
103    /// The current activity of the user.
104    pub game: Option<Activity>,
105    /// The ID of the guild.
106    pub guild_id: GuildId,
107    /// The online status of the user.
108    pub status: OnlineStatus,
109    /// The current activities of the user.
110    pub activities: Vec<Activity>,
111    /// The platform dependent status of the user.
112    pub client_status: ClientStatus,
113    /// When the user Nitro boosted the guild.
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub premium_since: Option<DateTime<FixedOffset>>,
116    /// The nickname of the user in the guild, if set.
117    #[serde(default, skip_serializing_if = "Option::is_none")]
118    pub nick: Option<String>,
119}
120
121impl crate::model::id::private::Sealed for Presence {}
122
123impl ToSnowflakeId for Presence {
124    type Id = <PartialUser as ToSnowflakeId>::Id;
125
126    fn id(&self) -> Self::Id {
127        self.user.id()
128    }
129}
130
131impl_eq_fields!(PartialUser: (a, b) => {
132    match (a, b) {
133        (PartialUser::Full(a), PartialUser::Full(b)) => assert_eq_fields!(a, b),
134        (PartialUser::Partial { id: id_a }, PartialUser::Partial { id: id_b }) => assert_eq_fields!(id_a, id_b),
135        (a, b) => panic_ne_fields!(a, b),
136    }
137});
138impl_eq_fields!(Presence: [
139    user,
140    roles,
141    game,
142    guild_id,
143    status,
144    activities,
145    client_status,
146    premium_since,
147    nick
148]);
149
150#[cfg(test)]
151mod tests {
152    use serde_json::json;
153
154    use crate::model::user::Discriminator;
155
156    use super::*;
157
158    #[test]
159    fn test_deserialize_partial_user() {
160        let value = json!({
161            "id": "80351110224678912"
162        });
163        let user = PartialUser::Partial {
164            id: UserId::from(80351110224678912),
165        };
166
167        let deserialized = PartialUser::deserialize(&value).unwrap();
168        assert_eq_fields!(user, deserialized);
169    }
170
171    #[test]
172    fn test_serialize_partial_user() {
173        let value = json!({
174            "id": "80351110224678912"
175        });
176        let user = PartialUser::Partial {
177            id: UserId::from(80351110224678912),
178        };
179
180        assert_eq!(value, serde_json::to_value(&user).unwrap());
181    }
182
183    #[test]
184    fn test_deserialize_full_user() {
185        let value = json!({
186            "id": "80351110224678912",
187            "username": "Nelly",
188            "discriminator": "1337",
189            "avatar": "8342729096ea3675442027381ff50dfe",
190        });
191        let user = PartialUser::Full(User {
192            id: UserId::from(80351110224678912),
193            name: "Nelly".to_owned(),
194            discriminator: Discriminator::new(1337).unwrap(),
195            avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()),
196            bot: false,
197            system: false,
198        });
199
200        let deserialized = PartialUser::deserialize(&value).unwrap();
201        assert_eq_fields!(user, deserialized);
202    }
203
204    #[test]
205    fn test_serialize_full_user() {
206        let value = json!({
207            "id": "80351110224678912",
208            "username": "Nelly",
209            "discriminator": "1337",
210            "avatar": "8342729096ea3675442027381ff50dfe"
211        });
212        let user = PartialUser::Full(User {
213            id: UserId::from(80351110224678912),
214            name: "Nelly".to_owned(),
215            discriminator: Discriminator::new(1337).unwrap(),
216            avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()),
217            bot: false,
218            system: false,
219        });
220
221        assert_eq!(value, serde_json::to_value(&user).unwrap());
222    }
223}