Skip to main content

twilight_http/request/scheduled_event/
update_guild_scheduled_event.rs

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