twilight_model/gateway/payload/incoming/
invite_create.rs

1//! Gateway event payload when an invite is created.
2
3use crate::{
4    guild::invite::TargetType,
5    id::{
6        marker::{ChannelMarker, GuildMarker, UserMarker},
7        Id,
8    },
9    user::{self, DiscriminatorDisplay, User},
10    util::{ImageHash, Timestamp},
11};
12use serde::{Deserialize, Serialize};
13
14/// A new [`Invite`] has been created.
15///
16/// [`Invite`]: crate::guild::invite::Invite
17#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
18pub struct InviteCreate {
19    /// ID of the channel invited users will first see.
20    pub channel_id: Id<ChannelMarker>,
21    /// Unique code.
22    pub code: String,
23    /// When the invite was created.
24    pub created_at: Timestamp,
25    /// ID of the guild being invited to.
26    pub guild_id: Id<GuildMarker>,
27    /// Information about the user who created the invite.
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub inviter: Option<User>,
30    /// Maximum age before the invite expires.
31    ///
32    /// This is in seconds.
33    pub max_age: u64,
34    /// Maximum number of uses before the invite expires.
35    pub max_uses: u64,
36    /// Target of the invite.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub target_user_type: Option<TargetType>,
39    /// User whose stream to display for this voice channel stream invite.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub target_user: Option<PartialUser>,
42    /// Whether the invite is temporary.
43    ///
44    /// Invited users will be kicked when they are disconnected from an audio
45    /// channel unless they're assigned a role.
46    pub temporary: bool,
47    /// Number of times the invite has been used.
48    ///
49    /// This will always be zero.
50    pub uses: u8,
51}
52
53/// Information about the user whose stream to display for a voice channel
54/// stream invite.
55#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
56pub struct PartialUser {
57    /// Hash of the user's avatar.
58    pub avatar: Option<ImageHash>,
59    /// Discriminator used to differentiate people with the same [`username`].
60    ///
61    /// [`username`]: Self::username
62    ///
63    /// # serde
64    ///
65    /// The discriminator field can be deserialized from either a string or an
66    /// integer. The field will always serialize into a string due to that being
67    /// the type Discord's API uses.
68    #[serde(with = "user::discriminator")]
69    pub discriminator: u16,
70    /// ID of the user.
71    pub id: Id<UserMarker>,
72    /// Username of the user.
73    pub username: String,
74}
75
76impl PartialUser {
77    /// Create a [`Display`] formatter for a user discriminator.
78    ///
79    /// [`Display`]: core::fmt::Display
80    pub const fn discriminator(&self) -> DiscriminatorDisplay {
81        DiscriminatorDisplay::new(self.discriminator)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::{InviteCreate, PartialUser};
88    use crate::{id::Id, test::image_hash, util::Timestamp};
89    use serde::{Deserialize, Serialize};
90    use serde_test::Token;
91    use static_assertions::{assert_fields, assert_impl_all};
92    use std::{fmt::Debug, hash::Hash};
93
94    assert_fields!(
95        InviteCreate: channel_id,
96        code,
97        created_at,
98        guild_id,
99        inviter,
100        max_age,
101        max_uses,
102        target_user_type,
103        target_user,
104        temporary,
105        uses
106    );
107    assert_fields!(PartialUser: avatar, discriminator, id, username);
108    assert_impl_all!(
109        InviteCreate: Clone,
110        Debug,
111        Deserialize<'static>,
112        Eq,
113        Hash,
114        PartialEq,
115        Send,
116        Serialize,
117        Sync
118    );
119    assert_impl_all!(
120        PartialUser: Clone,
121        Debug,
122        Deserialize<'static>,
123        Eq,
124        Hash,
125        PartialEq,
126        Send,
127        Serialize,
128        Sync
129    );
130
131    #[test]
132    fn invite_create() {
133        let created_at = Timestamp::from_secs(1_609_459_200).expect("non zero");
134
135        let value = InviteCreate {
136            channel_id: Id::new(1),
137            code: "a".repeat(7),
138            created_at,
139            guild_id: Id::new(2),
140            inviter: None,
141            max_age: 3600,
142            max_uses: 5,
143            target_user_type: None,
144            target_user: None,
145            temporary: false,
146            uses: 0,
147        };
148
149        serde_test::assert_tokens(
150            &value,
151            &[
152                Token::Struct {
153                    name: "InviteCreate",
154                    len: 8,
155                },
156                Token::Str("channel_id"),
157                Token::NewtypeStruct { name: "Id" },
158                Token::Str("1"),
159                Token::Str("code"),
160                Token::Str("aaaaaaa"),
161                Token::Str("created_at"),
162                Token::Str("2021-01-01T00:00:00.000000+00:00"),
163                Token::Str("guild_id"),
164                Token::NewtypeStruct { name: "Id" },
165                Token::Str("2"),
166                Token::Str("max_age"),
167                Token::U64(3600),
168                Token::Str("max_uses"),
169                Token::U64(5),
170                Token::Str("temporary"),
171                Token::Bool(false),
172                Token::Str("uses"),
173                Token::U8(0),
174                Token::StructEnd,
175            ],
176        );
177    }
178
179    #[test]
180    fn partial_user() {
181        let value = PartialUser {
182            avatar: Some(image_hash::AVATAR),
183            discriminator: 123,
184            id: Id::new(1),
185            username: "twilight".to_owned(),
186        };
187
188        serde_test::assert_tokens(
189            &value,
190            &[
191                Token::Struct {
192                    name: "PartialUser",
193                    len: 4,
194                },
195                Token::Str("avatar"),
196                Token::Some,
197                Token::Str(image_hash::AVATAR_INPUT),
198                Token::Str("discriminator"),
199                Token::Str("0123"),
200                Token::Str("id"),
201                Token::NewtypeStruct { name: "Id" },
202                Token::Str("1"),
203                Token::Str("username"),
204                Token::Str("twilight"),
205                Token::StructEnd,
206            ],
207        );
208    }
209}