Skip to main content

twilight_http/request/scheduled_event/
update_guild_scheduled_event.rs

1use super::EntityMetadataFields;
2use crate::{
3    client::Client,
4    error::Error,
5    request::{AuditLogReason, Nullable, Request, TryIntoRequest},
6    response::{Response, ResponseFuture},
7    routing::Route,
8};
9use serde::Serialize;
10use std::future::IntoFuture;
11use twilight_model::{
12    guild::scheduled_event::{
13        EntityType, GuildScheduledEvent, PrivacyLevel, Status, recurrence_rule::RecurrenceRule,
14    },
15    id::{
16        Id,
17        marker::{ChannelMarker, GuildMarker, ScheduledEventMarker},
18    },
19    util::Timestamp,
20};
21use twilight_validate::request::{
22    ValidationError, audit_reason as validate_audit_reason,
23    scheduled_event_description as validate_scheduled_event_description,
24    scheduled_event_name as validate_scheduled_event_name,
25};
26
27#[derive(Serialize)]
28struct UpdateGuildScheduledEventFields<'a> {
29    #[serde(skip_serializing_if = "Option::is_none")]
30    channel_id: Option<Nullable<Id<ChannelMarker>>>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    description: Option<Nullable<&'a str>>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    entity_metadata: Option<EntityMetadataFields<'a>>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    entity_type: Option<EntityType>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    image: Option<Nullable<&'a str>>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    name: Option<&'a str>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    privacy_level: Option<PrivacyLevel>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    recurrence_rule: Option<Nullable<&'a RecurrenceRule>>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    scheduled_end_time: Option<Nullable<&'a Timestamp>>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    scheduled_start_time: Option<&'a Timestamp>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    status: Option<Status>,
51}
52
53/// Update a scheduled event in a guild.
54///
55/// This endpoint supports changing the type of event. When changing the entity
56/// type to either [`EntityType::StageInstance`] or [`EntityType::Voice`], an
57/// [`Id<ChannelMarker>`] must be provided if it does not already exist.
58///
59/// When changing the entity type to [`EntityType::External`], the `channel_id`
60/// field is cleared and the [`channel_id`] method has no effect.  Additionally,
61/// you must set a location with [`location`].
62///
63/// [`channel_id`]: UpdateGuildScheduledEvent::channel_id
64/// [`location`]: UpdateGuildScheduledEvent::location
65/// [`channel_id`]: UpdateGuildScheduledEvent::channel_id
66/// [`location`]: UpdateGuildScheduledEvent::location
67#[must_use = "requests must be configured and executed"]
68pub struct UpdateGuildScheduledEvent<'a> {
69    guild_id: Id<GuildMarker>,
70    http: &'a Client,
71    fields: Result<UpdateGuildScheduledEventFields<'a>, ValidationError>,
72    reason: Result<Option<&'a str>, ValidationError>,
73    scheduled_event_id: Id<ScheduledEventMarker>,
74}
75
76impl<'a> UpdateGuildScheduledEvent<'a> {
77    pub(crate) const fn new(
78        http: &'a Client,
79        guild_id: Id<GuildMarker>,
80        scheduled_event_id: Id<ScheduledEventMarker>,
81    ) -> Self {
82        Self {
83            guild_id,
84            http,
85            fields: Ok(UpdateGuildScheduledEventFields {
86                channel_id: None,
87                description: None,
88                entity_metadata: None,
89                entity_type: None,
90                image: None,
91                name: None,
92                privacy_level: None,
93                recurrence_rule: None,
94                scheduled_end_time: None,
95                scheduled_start_time: None,
96                status: None,
97            }),
98            reason: Ok(None),
99            scheduled_event_id,
100        }
101    }
102
103    /// Set the channel ID.
104    ///
105    /// If `entity_type` is already [`EntityType::External`], this has no
106    /// effect.
107    pub fn channel_id(mut self, channel_id: Id<ChannelMarker>) -> Self {
108        if let Ok(fields) = self.fields.as_mut()
109            && fields.entity_type != Some(EntityType::External)
110        {
111            fields.channel_id = Some(Nullable(Some(channel_id)));
112        }
113
114        self
115    }
116
117    /// Set the description of the event.
118    ///
119    /// Must be between 1 and 1000 characters in length.
120    ///
121    /// # Errors
122    ///
123    /// Returns an error of type [`ScheduledEventDescription`] if the
124    /// description is invalid.
125    ///
126    /// [`ScheduledEventDescription`]: twilight_validate::request::ValidationErrorType::ScheduledEventDescription
127    pub fn description(mut self, description: Option<&'a str>) -> Self {
128        self.fields = self.fields.and_then(|mut fields| {
129            if let Some(description) = description {
130                validate_scheduled_event_description(description)?;
131            }
132
133            fields.description = Some(Nullable(description));
134
135            Ok(fields)
136        });
137
138        self
139    }
140
141    /// Set the [`EntityType`] of the scheduled event.
142    ///
143    /// See the struct-level documentation for information about required fields
144    /// for each type.
145    pub fn entity_type(mut self, entity_type: EntityType) -> Self {
146        self.fields = self.fields.map(|mut fields| {
147            if entity_type == EntityType::External {
148                fields.channel_id = None;
149            }
150
151            fields.entity_type = Some(entity_type);
152
153            fields
154        });
155
156        self
157    }
158
159    /// Set the cover image of the event.
160    ///
161    /// Pass [`None`] to clear the image.
162    ///
163    /// This must be a Data URI, in the form of
164    /// `data:image/{type};base64,{data}` where `{type}` is the image MIME type
165    /// and `{data}` is the base64-encoded image. See [Discord Docs/Image Data].
166    ///
167    /// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
168    pub fn image(mut self, image: Option<&'a str>) -> Self {
169        self.fields = self.fields.map(|mut fields| {
170            fields.image = Some(Nullable(image));
171
172            fields
173        });
174
175        self
176    }
177
178    /// Set the location of the external scheduled event.
179    ///
180    /// This only functions if the event's [`EntityType`] is [`External`].
181    ///
182    /// [`External`]: EntityType::External
183    pub fn location(mut self, location: Option<&'a str>) -> Self {
184        self.fields = self.fields.map(|mut fields| {
185            fields.entity_metadata = Some(EntityMetadataFields { location });
186
187            fields
188        });
189
190        self
191    }
192
193    /// Set the name of the event.
194    ///
195    /// Must be between 1 and 100 characters in length.
196    ///
197    /// # Errors
198    ///
199    /// Returns an error of type [`ScheduledEventName`] if the name is invalid.
200    ///
201    /// [`ScheduledEventName`]: twilight_validate::request::ValidationErrorType::ScheduledEventName
202    pub fn name(mut self, name: &'a str) -> Self {
203        self.fields = self.fields.and_then(|mut fields| {
204            validate_scheduled_event_name(name)?;
205
206            fields.name = Some(name);
207
208            Ok(fields)
209        });
210
211        self
212    }
213
214    /// Set the recurrence rule of the event.
215    ///
216    /// Pass `None` to clear the recurrence rule.
217    pub fn recurrence_rule(mut self, recurrence_rule: Option<&'a RecurrenceRule>) -> Self {
218        self.fields = self.fields.map(|mut fields| {
219            fields.recurrence_rule = Some(Nullable(recurrence_rule));
220
221            fields
222        });
223
224        self
225    }
226
227    /// Set the scheduled end time of the event.
228    ///
229    /// Required for external events.
230    pub fn scheduled_end_time(mut self, scheduled_end_time: Option<&'a Timestamp>) -> Self {
231        self.fields = self.fields.map(|mut fields| {
232            fields.scheduled_end_time = Some(Nullable(scheduled_end_time));
233
234            fields
235        });
236
237        self
238    }
239
240    /// Set the scheduled start time of the event.
241    pub fn scheduled_start_time(mut self, scheduled_start_time: &'a Timestamp) -> Self {
242        self.fields = self.fields.map(|mut fields| {
243            fields.scheduled_start_time = Some(scheduled_start_time);
244
245            fields
246        });
247
248        self
249    }
250
251    /// Set the status of the event.
252    ///
253    /// If an event is currently [`Scheduled`], it can only be set to [`Active`]
254    /// or [`Cancelled`]. If it is currently [`Active`], it can only be set to
255    /// [`Completed`]. Otherwise, the status can not be updated.
256    ///
257    /// [`Active`]: Status::Active
258    /// [`Cancelled`]: Status::Cancelled
259    /// [`Completed`]: Status::Completed
260    /// [`Scheduled`]: Status::Scheduled
261    pub fn status(mut self, status: Status) -> Self {
262        self.fields = self.fields.map(|mut fields| {
263            fields.status = Some(status);
264
265            fields
266        });
267
268        self
269    }
270}
271
272impl<'a> AuditLogReason<'a> for UpdateGuildScheduledEvent<'a> {
273    fn reason(mut self, reason: &'a str) -> Self {
274        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
275
276        self
277    }
278}
279
280impl IntoFuture for UpdateGuildScheduledEvent<'_> {
281    type Output = Result<Response<GuildScheduledEvent>, Error>;
282
283    type IntoFuture = ResponseFuture<GuildScheduledEvent>;
284
285    fn into_future(self) -> Self::IntoFuture {
286        let http = self.http;
287
288        match self.try_into_request() {
289            Ok(request) => http.request(request),
290            Err(source) => ResponseFuture::error(source),
291        }
292    }
293}
294
295impl TryIntoRequest for UpdateGuildScheduledEvent<'_> {
296    fn try_into_request(self) -> Result<Request, Error> {
297        let fields = self.fields.map_err(Error::validation)?;
298
299        Request::builder(&Route::UpdateGuildScheduledEvent {
300            guild_id: self.guild_id.get(),
301            scheduled_event_id: self.scheduled_event_id.get(),
302        })
303        .json(&fields)
304        .build()
305    }
306}