twilight_model/guild/scheduled_event/
mod.rs

1//! Types for interacting with scheduled events.
2
3mod user;
4
5pub use self::user::GuildScheduledEventUser;
6
7use crate::{
8    id::{
9        marker::{
10            ChannelMarker, GuildMarker, ScheduledEventEntityMarker, ScheduledEventMarker,
11            UserMarker,
12        },
13        Id,
14    },
15    user::User,
16    util::{ImageHash, Timestamp},
17};
18use serde::{Deserialize, Serialize};
19
20/// Representation of a scheduled event.
21///
22/// For events created before October 25th, 2021, [`creator`] and [`creator_id`]
23/// will be [`None`].
24///
25/// [`creator`]: Self::creator
26/// [`creator_id`]: Self::creator_id
27#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
28pub struct GuildScheduledEvent {
29    /// ID of the stage or voice channel if there is one.
30    ///
31    /// Present on events of [`EntityType::StageInstance`] and
32    /// [`EntityType::Voice`].
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub channel_id: Option<Id<ChannelMarker>>,
35    /// User object of the event's creator.
36    ///
37    /// Only present on events created after October 25th, 2021.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub creator: Option<User>,
40    /// ID of the event's creator.
41    ///
42    /// Only present on events created after October 25th, 2021.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub creator_id: Option<Id<UserMarker>>,
45    /// Description of the event.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub description: Option<String>,
48    /// ID of the event's entity.
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub entity_id: Option<Id<ScheduledEventEntityMarker>>,
51    /// Metadata of an entity, if it exists.
52    ///
53    /// Currently, only present on events of [`EntityType::External`].
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub entity_metadata: Option<EntityMetadata>,
56    /// Type of entity associated with the event.
57    pub entity_type: EntityType,
58    /// ID of the guild the event takes place in.
59    pub guild_id: Id<GuildMarker>,
60    /// ID of the event.
61    pub id: Id<ScheduledEventMarker>,
62    /// Hash of the event's cover image, if it has one.
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub image: Option<ImageHash>,
65    /// Name of the event.
66    pub name: String,
67    /// Privacy level of the event.
68    pub privacy_level: PrivacyLevel,
69    /// Scheduled end time of the event.
70    ///
71    /// Required on events of type [`EntityType::External`]. It also may be
72    /// present in other event types.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub scheduled_end_time: Option<Timestamp>,
75    /// Scheduled start time of the event.
76    pub scheduled_start_time: Timestamp,
77    /// Status of the event.
78    pub status: Status,
79    /// Number of users subscribed to the event.
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub user_count: Option<u64>,
82}
83
84/// Metadata associated with an event.
85#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
86pub struct EntityMetadata {
87    /// Physical location of an event with type [`EntityType::External`].
88    pub location: Option<String>,
89}
90
91/// Type of event.
92#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
93#[non_exhaustive]
94#[serde(from = "u8", into = "u8")]
95pub enum EntityType {
96    /// Event takes place in a stage instance.
97    StageInstance,
98    /// Event takes place in a voice channel.
99    Voice,
100    /// Event takes place outside of Discord.
101    External,
102    /// Variant value is unknown to the library.
103    Unknown(u8),
104}
105
106impl From<u8> for EntityType {
107    fn from(value: u8) -> Self {
108        match value {
109            1 => EntityType::StageInstance,
110            2 => EntityType::Voice,
111            3 => EntityType::External,
112            unknown => EntityType::Unknown(unknown),
113        }
114    }
115}
116
117impl From<EntityType> for u8 {
118    fn from(value: EntityType) -> Self {
119        match value {
120            EntityType::StageInstance => 1,
121            EntityType::Voice => 2,
122            EntityType::External => 3,
123            EntityType::Unknown(unknown) => unknown,
124        }
125    }
126}
127
128/// Privacy level of an event.
129#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
130#[non_exhaustive]
131#[serde(from = "u8", into = "u8")]
132pub enum PrivacyLevel {
133    /// Event is only accessible to guild members.
134    GuildOnly,
135    /// Variant value is unknown to the library.
136    Unknown(u8),
137}
138
139impl From<u8> for PrivacyLevel {
140    fn from(value: u8) -> Self {
141        match value {
142            2 => PrivacyLevel::GuildOnly,
143            unknown => PrivacyLevel::Unknown(unknown),
144        }
145    }
146}
147
148impl From<PrivacyLevel> for u8 {
149    fn from(value: PrivacyLevel) -> Self {
150        match value {
151            PrivacyLevel::GuildOnly => 2,
152            PrivacyLevel::Unknown(unknown) => unknown,
153        }
154    }
155}
156
157/// Status of an event.
158#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
159#[non_exhaustive]
160#[serde(from = "u8", into = "u8")]
161pub enum Status {
162    /// Event is scheduled.
163    ///
164    /// With this status, the event can either be made active or cancelled.
165    Scheduled,
166    /// Event is active.
167    ///
168    /// With this status, the event can only be made complete.
169    Active,
170    /// Event is complete.
171    Completed,
172    /// Event is cancelled.
173    Cancelled,
174    /// Variant value is unknown to the library.
175    Unknown(u8),
176}
177
178impl From<u8> for Status {
179    fn from(value: u8) -> Self {
180        match value {
181            1 => Status::Scheduled,
182            2 => Status::Active,
183            3 => Status::Completed,
184            4 => Status::Cancelled,
185            unknown => Status::Unknown(unknown),
186        }
187    }
188}
189
190impl From<Status> for u8 {
191    fn from(value: Status) -> Self {
192        match value {
193            Status::Scheduled => 1,
194            Status::Active => 2,
195            Status::Completed => 3,
196            Status::Cancelled => 4,
197            Status::Unknown(unknown) => unknown,
198        }
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use crate::test::image_hash::{COVER, COVER_INPUT};
206    use serde_test::Token;
207    use std::error::Error;
208
209    #[test]
210    fn scheduled_event() -> Result<(), Box<dyn Error>> {
211        let scheduled_start_time = Timestamp::parse("2022-01-01T00:00:00.000000+00:00")?;
212
213        let value = GuildScheduledEvent {
214            channel_id: Some(Id::new(1)),
215            creator: None,
216            creator_id: None,
217            description: Some("this is a dance party for garfield lovers".into()),
218            entity_id: Some(Id::new(2)),
219            entity_metadata: None,
220            entity_type: EntityType::StageInstance,
221            guild_id: Id::new(3),
222            id: Id::new(4),
223            image: Some(COVER),
224            name: "garfield dance party".into(),
225            privacy_level: PrivacyLevel::GuildOnly,
226            scheduled_end_time: None,
227            scheduled_start_time,
228            status: Status::Completed,
229            user_count: Some(1),
230        };
231
232        serde_test::assert_tokens(
233            &value,
234            &[
235                Token::Struct {
236                    name: "GuildScheduledEvent",
237                    len: 12,
238                },
239                Token::Str("channel_id"),
240                Token::Some,
241                Token::NewtypeStruct { name: "Id" },
242                Token::Str("1"),
243                Token::Str("description"),
244                Token::Some,
245                Token::Str("this is a dance party for garfield lovers"),
246                Token::Str("entity_id"),
247                Token::Some,
248                Token::NewtypeStruct { name: "Id" },
249                Token::Str("2"),
250                Token::Str("entity_type"),
251                Token::U8(1),
252                Token::Str("guild_id"),
253                Token::NewtypeStruct { name: "Id" },
254                Token::Str("3"),
255                Token::Str("id"),
256                Token::NewtypeStruct { name: "Id" },
257                Token::Str("4"),
258                Token::Str("image"),
259                Token::Some,
260                Token::Str(COVER_INPUT),
261                Token::Str("name"),
262                Token::Str("garfield dance party"),
263                Token::Str("privacy_level"),
264                Token::U8(2),
265                Token::Str("scheduled_start_time"),
266                Token::Str("2022-01-01T00:00:00.000000+00:00"),
267                Token::Str("status"),
268                Token::U8(3),
269                Token::Str("user_count"),
270                Token::Some,
271                Token::U64(1),
272                Token::StructEnd,
273            ],
274        );
275
276        Ok(())
277    }
278}