robespierre_models/
users.rs

1use bitflags::bitflags;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    autumn::{Attachment, AttachmentId},
6    id::UserId,
7};
8
9/*
10Types
11*/
12
13// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L4-L10
14
15/// Username
16#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[serde(transparent)]
18pub struct Username(pub String);
19
20// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L12-L23
21
22/// Your relationship with the user
23#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
24pub enum RelationshipStatus {
25    None,
26    User,
27    Friend,
28    Outgoing,
29    Incoming,
30    Blocked,
31    BlockedOther,
32}
33
34// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L25-L27
35// pub struct RelationshipOnly {
36//     status: RelationshipStatus
37// }
38
39// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L29-L34
40
41#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
42#[serde(deny_unknown_fields)]
43pub struct Relationship {
44    pub status: RelationshipStatus,
45    /// Other user's ID
46    #[serde(rename = "_id")]
47    pub id: UserId,
48}
49
50// https://github.com/revoltchat/api/blob/master/types/Users.ts#L36-L44
51
52/// User presence
53#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
54#[serde(deny_unknown_fields)]
55pub enum UserPresence {
56    Online,
57    Idle,
58    Busy,
59    Invisible,
60}
61
62// https://github.com/revoltchat/api/blob/master/types/Users.ts#L46-L58
63
64#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
65#[serde(deny_unknown_fields)]
66pub struct Status {
67    /// Status text.
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub text: Option<String>,
70    /// User presence, like online, invisible, idle, or busy.
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub presence: Option<UserPresence>,
73}
74
75// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L60-L67
76
77bitflags! {
78    #[derive(Serialize, Deserialize)]
79    #[serde(transparent)]
80    #[doc = "User badges"]
81    pub struct Badges: u32 {
82        const DEVELOPER = 1;
83        const TRANSLATOR = 2;
84        const SUPPORTER = 4;
85        const RESPONSIBLE_DISCLOSURE = 8;
86        const REVOLT_TEAM = 16;
87        const EARLY_ADOPTER = 256;
88    }
89}
90
91// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L69-L77
92
93/// Bot information
94#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
95#[serde(deny_unknown_fields)]
96pub struct BotInformation {
97    /// The User ID of the owner of this bot
98    pub owner: UserId,
99}
100
101// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L79-L128
102
103/// An user
104#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
105#[serde(deny_unknown_fields)]
106pub struct User {
107    /// The user id.
108    #[serde(rename = "_id")]
109    pub id: UserId,
110    /// The username
111    pub username: Username,
112    /// The avatar (if available).
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub avatar: Option<Attachment>,
115    /// The relationships with other users.
116    ///
117    /// Note: this is only available is this user
118    /// is the currently logged-in user.
119    #[serde(default, skip_serializing_if = "Vec::is_empty")]
120    pub relations: Vec<Relationship>,
121    /// The badges of the user
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub badges: Option<Badges>,
124    /// The current status
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub status: Option<Status>,
127    /// The relationship the currently logged-in user has
128    /// with this user.
129    #[serde(default, skip_serializing_if = "Option::is_none")]
130    pub relationship: Option<RelationshipStatus>,
131    /// Whether this user is online or not.
132    #[serde(default, skip_serializing_if = "Option::is_none")]
133    pub online: Option<bool>,
134    /// Refer to [`UserFlags`].
135    #[serde(default, skip_serializing_if = "Option::is_none")]
136    pub flags: Option<UserFlags>,
137    /// If this is a bot, then [`BotInfo`] will
138    /// contain the user id of the owner of the bot.
139    /// If it is not a bot, then it will be [`None`]
140    #[serde(default, skip_serializing_if = "Option::is_none")]
141    pub bot: Option<BotInformation>,
142
143    /// Profile data
144    /// Sometimes the server sends it although fetching
145    /// an user doesn't retrieve the profile data too.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub profile: Option<Profile>,
148}
149
150// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L115-L122
151
152bitflags! {
153    #[derive(Serialize, Deserialize)]
154    #[serde(transparent)]
155    #[doc = "User flags"]
156    pub struct UserFlags: u32 {
157        const SUSPENDED = 0x1;
158        const DELETED = 0x2;
159        const BANNED = 0x4;
160    }
161}
162
163// https://github.com/revoltchat/api/blob/097f40e37108cd3a1816b1c2cc69a137ae317069/types/Users.ts#L130-L142
164
165/// Profile data about an user.
166#[derive(Serialize, Deserialize, Default, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
167#[serde(deny_unknown_fields)]
168pub struct Profile {
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub content: Option<String>,
171    #[serde(default, skip_serializing_if = "Option::is_none")]
172    pub background: Option<Attachment>,
173}
174
175/*
176Extra
177*/
178
179/// An user struct where all the fields are optional, and can be
180/// used to update an [`User`] with the [`PartialUser::patch`]
181/// function.
182#[derive(Serialize, Deserialize, Default, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
183#[serde(deny_unknown_fields)]
184pub struct UserPatch {
185    #[serde(rename = "_id", default, skip_serializing_if = "Option::is_none")]
186    pub id: Option<UserId>,
187    #[serde(default, skip_serializing_if = "Option::is_none")]
188    pub username: Option<Username>,
189    #[serde(default, skip_serializing_if = "Option::is_none")]
190    pub avatar: Option<Attachment>,
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub relations: Option<Vec<Relationship>>,
193    #[serde(default, skip_serializing_if = "Option::is_none")]
194    pub badges: Option<Badges>,
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub status: Option<Status>,
197    #[serde(default, skip_serializing_if = "Option::is_none")]
198    pub relationship: Option<RelationshipStatus>,
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub online: Option<bool>,
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub flags: Option<UserFlags>,
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub bot: Option<BotInformation>,
205    #[serde(
206        rename = "profile.content",
207        default,
208        skip_serializing_if = "Option::is_none"
209    )]
210    pub profile_content: Option<String>,
211    #[serde(
212        rename = "profile.background",
213        default,
214        skip_serializing_if = "Option::is_none"
215    )]
216    pub profile_background: Option<Attachment>,
217}
218
219impl UserPatch {
220    /// Treats the [`PartialUser`] as a list of modifications
221    /// that have to be applied to the given user, and applies
222    /// them.
223    pub fn patch(self, user: &mut User) {
224        let UserPatch {
225            id: pid,
226            username: pusername,
227            avatar: pavatar,
228            relations: prelations,
229            badges: pbadges,
230            status: pstatus,
231            relationship: prelationship,
232            online: ponline,
233            flags: pflags,
234            bot: pbot,
235            profile_content: pprofile_content,
236            profile_background: pprofile_background,
237        } = self;
238        let User {
239            id,
240            username,
241            avatar,
242            relations,
243            badges,
244            status,
245            relationship,
246            online,
247            flags,
248            bot,
249            profile,
250        } = user;
251
252        if let Some(pid) = pid {
253            *id = pid;
254        }
255        if let Some(pusername) = pusername {
256            *username = pusername;
257        }
258        if let Some(pavatar) = pavatar {
259            *avatar = Some(pavatar);
260        }
261        if let Some(prelations) = prelations {
262            *relations = prelations;
263        }
264        if let Some(pbadges) = pbadges {
265            *badges = Some(pbadges);
266        }
267        if let Some(pstatus) = pstatus {
268            *status = Some(pstatus);
269        }
270        if let Some(prelationship) = prelationship {
271            *relationship = Some(prelationship);
272        }
273        if let Some(ponline) = ponline {
274            *online = Some(ponline);
275        }
276        if let Some(pflags) = pflags {
277            *flags = Some(pflags);
278        }
279        if let Some(pbot) = pbot {
280            *bot = Some(pbot);
281        }
282        if let Some(pprofile_content) = pprofile_content {
283            profile.get_or_insert(Default::default()).content = Some(pprofile_content);
284        }
285        if let Some(pprofile_background) = pprofile_background {
286            profile.get_or_insert(Default::default()).background = Some(pprofile_background);
287        }
288    }
289}
290
291/// An user field.
292#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
293pub enum UserField {
294    Avatar,
295    ProfileBackground,
296    ProfileContent,
297    StatusText,
298}
299
300impl UserField {
301    /// Removes this field from the user.
302    pub fn remove_patch(self, user: &mut User) {
303        match self {
304            Self::Avatar => user.avatar = None,
305            Self::ProfileBackground => {}
306            Self::ProfileContent => {}
307            Self::StatusText => {
308                if let Some(Status { text, .. }) = &mut user.status {
309                    *text = None
310                }
311            }
312        }
313    }
314}
315
316/// A patch to the profile data of an user.
317#[derive(Serialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
318pub struct UserProfileDataPatch {
319    #[serde(default, skip_serializing_if = "Option::is_none")]
320    pub content: Option<String>,
321    #[serde(default, skip_serializing_if = "Option::is_none")]
322    pub background: Option<AttachmentId>,
323}
324
325/// A patch to an user.
326#[derive(Serialize, Default, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
327#[non_exhaustive]
328pub struct UserEditPatch {
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub status: Option<Status>,
331    #[serde(skip_serializing_if = "Option::is_none")]
332    pub profile: Option<UserProfileDataPatch>,
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub avatar: Option<AttachmentId>,
335    #[serde(skip_serializing_if = "Option::is_none")]
336    pub remove: Option<UserField>,
337}