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