Skip to main content

twilight_model/guild/scheduled_event/
mod.rs

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