twilight_http/request/channel/thread/create_forum_thread/
message.rs

1use super::{CreateForumThread, ForumThread};
2use crate::{
3    request::{attachment::PartialAttachment, Nullable, TryIntoRequest},
4    response::{Response, ResponseFuture},
5    Error,
6};
7use serde::Serialize;
8use std::{future::IntoFuture, mem};
9use twilight_model::{
10    channel::message::{AllowedMentions, Component, Embed, MessageFlags},
11    http::attachment::Attachment,
12    id::{marker::StickerMarker, Id},
13};
14use twilight_validate::message::{
15    attachment_filename as validate_attachment_filename, components as validate_components,
16    content as validate_content, embeds as validate_embeds, sticker_ids as validate_sticker_ids,
17    MessageValidationError,
18};
19
20/// Contents of the first message in the new forum thread.
21#[derive(Serialize)]
22pub(super) struct CreateForumThreadMessageFields<'a> {
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub(super) allowed_mentions: Option<Nullable<&'a AllowedMentions>>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub(super) attachments: Option<Vec<PartialAttachment<'a>>>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub(super) components: Option<&'a [Component]>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub(super) content: Option<&'a str>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub(super) embeds: Option<&'a [Embed]>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub(super) flags: Option<MessageFlags>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub(super) payload_json: Option<&'a [u8]>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub(super) sticker_ids: Option<&'a [Id<StickerMarker>]>,
39}
40
41#[must_use = "requests must be configured and executed"]
42pub struct CreateForumThreadMessage<'a>(Result<CreateForumThread<'a>, MessageValidationError>);
43
44impl<'a> CreateForumThreadMessage<'a> {
45    pub(super) const fn new(inner: CreateForumThread<'a>) -> Self {
46        Self(Ok(inner))
47    }
48
49    /// Specify the [`AllowedMentions`] for the message.
50    ///
51    /// Unless otherwise called, the request will use the client's default
52    /// allowed mentions. Set to `None` to ignore this default.
53    pub fn allowed_mentions(mut self, allowed_mentions: Option<&'a AllowedMentions>) -> Self {
54        if let Ok(inner) = self.0.as_mut() {
55            inner.fields.message.allowed_mentions = Some(Nullable(allowed_mentions));
56        }
57
58        self
59    }
60
61    /// Attach multiple files to the message.
62    ///
63    /// Calling this method will clear any previous calls.
64    ///
65    /// # Errors
66    ///
67    /// Returns an error of type [`AttachmentFilename`] if any filename is
68    /// invalid.
69    ///
70    /// [`AttachmentFilename`]: twilight_validate::message::MessageValidationErrorType::AttachmentFilename
71    pub fn attachments(mut self, attachments: &'a [Attachment]) -> Self {
72        if self.0.is_ok() {
73            let validation = attachments
74                .iter()
75                .try_for_each(|attachment| validate_attachment_filename(&attachment.filename));
76
77            if let Err(source) = validation {
78                self.0 = Err(source);
79            } else if let Ok(inner) = self.0.as_mut() {
80                let mut manager = mem::take(&mut inner.attachment_manager);
81                manager = manager.set_files(attachments.iter().collect());
82
83                inner.attachment_manager = manager;
84            }
85        }
86
87        self
88    }
89
90    /// Set the message's list of [`Component`]s.
91    ///
92    /// Calling this method will clear previous calls.
93    ///
94    /// Requires a webhook owned by the application.
95    ///
96    /// # Errors
97    ///
98    /// Refer to the errors section of
99    /// [`twilight_validate::component::component`] for a list of errors that
100    /// may be returned as a result of validating each provided component.
101    pub fn components(mut self, components: &'a [Component]) -> Self {
102        self.0 = self.0.and_then(|mut inner| {
103            validate_components(
104                components,
105                inner
106                    .fields
107                    .message
108                    .flags
109                    .is_some_and(|f| f.contains(MessageFlags::IS_COMPONENTS_V2)),
110            )?;
111            inner.fields.message.components = Some(components);
112
113            Ok(inner)
114        });
115
116        self
117    }
118
119    /// Set the message's content.
120    ///
121    /// The maximum length is 2000 UTF-16 characters.
122    ///
123    /// # Errors
124    ///
125    /// Returns an error of type [`ContentInvalid`] if the content length is too
126    /// long.
127    ///
128    /// [`ContentInvalid`]: twilight_validate::message::MessageValidationErrorType::ContentInvalid
129    pub fn content(mut self, content: &'a str) -> Self {
130        self.0 = self.0.and_then(|mut inner| {
131            validate_content(content)?;
132            inner.fields.message.content = Some(content);
133
134            Ok(inner)
135        });
136
137        self
138    }
139
140    /// Set the message's list of embeds.
141    ///
142    /// Calling this method will clear previous calls.
143    ///
144    /// The amount of embeds must not exceed [`EMBED_COUNT_LIMIT`]. The total
145    /// character length of each embed must not exceed [`EMBED_TOTAL_LENGTH`]
146    /// characters. Additionally, the internal fields also have character
147    /// limits. Refer to [Discord Docs/Embed Limits] for more information.
148    ///
149    /// # Errors
150    ///
151    /// Returns an error of type [`TooManyEmbeds`] if there are too many embeds.
152    ///
153    /// Otherwise, refer to the errors section of
154    /// [`twilight_validate::embed::embed`] for a list of errors that may occur.
155    ///
156    /// [Discord Docs/Embed Limits]: https://discord.com/developers/docs/resources/channel#embed-limits
157    /// [`EMBED_COUNT_LIMIT`]: twilight_validate::message::EMBED_COUNT_LIMIT
158    /// [`EMBED_TOTAL_LENGTH`]: twilight_validate::embed::EMBED_TOTAL_LENGTH
159    /// [`TooManyEmbeds`]: twilight_validate::message::MessageValidationErrorType::TooManyEmbeds
160    pub fn embeds(mut self, embeds: &'a [Embed]) -> Self {
161        self.0 = self.0.and_then(|mut inner| {
162            validate_embeds(embeds)?;
163            inner.fields.message.embeds = Some(embeds);
164
165            Ok(inner)
166        });
167
168        self
169    }
170
171    /// Set the message's flags.
172    ///
173    /// The only supported flags are [`SUPPRESS_EMBEDS`] and
174    /// [`SUPPRESS_NOTIFICATIONS`].
175    ///
176    /// [`SUPPRESS_EMBEDS`]: MessageFlags::SUPPRESS_EMBEDS
177    /// [`SUPPRESS_NOTIFICATIONS`]: MessageFlags::SUPPRESS_NOTIFICATIONS
178    pub fn flags(mut self, flags: MessageFlags) -> Self {
179        if let Ok(inner) = self.0.as_mut() {
180            inner.fields.message.flags = Some(flags);
181        }
182
183        self
184    }
185
186    /// JSON encoded body of any additional request fields.
187    ///
188    /// If this method is called, all other fields are ignored, except for
189    /// [`attachments`]. See [Discord Docs/Uploading Files].
190    ///
191    /// # Examples
192    ///
193    /// See [`ExecuteWebhook::payload_json`] for examples.
194    ///
195    /// [Discord Docs/Uploading Files]: https://discord.com/developers/docs/reference#uploading-files
196    /// [`ExecuteWebhook::payload_json`]: crate::request::channel::webhook::ExecuteWebhook::payload_json
197    /// [`attachments`]: Self::attachments
198    pub fn payload_json(mut self, payload_json: &'a [u8]) -> Self {
199        if let Ok(inner) = self.0.as_mut() {
200            inner.fields.message.payload_json = Some(payload_json);
201        }
202
203        self
204    }
205
206    /// Set the IDs of up to 3 guild stickers.
207    ///
208    /// # Errors
209    ///
210    /// Returns an error of type [`StickersInvalid`] if the length is invalid.
211    ///
212    /// [`StickersInvalid`]: twilight_validate::message::MessageValidationErrorType::StickersInvalid
213    pub fn sticker_ids(mut self, sticker_ids: &'a [Id<StickerMarker>]) -> Self {
214        self.0 = self.0.and_then(|mut inner| {
215            validate_sticker_ids(sticker_ids)?;
216            inner.fields.message.sticker_ids = Some(sticker_ids);
217
218            Ok(inner)
219        });
220
221        self
222    }
223}
224
225impl IntoFuture for CreateForumThreadMessage<'_> {
226    type Output = Result<Response<ForumThread>, Error>;
227
228    type IntoFuture = ResponseFuture<ForumThread>;
229
230    fn into_future(self) -> Self::IntoFuture {
231        match self.0 {
232            Ok(inner) => inner.exec(),
233            Err(source) => ResponseFuture::error(Error::validation(source)),
234        }
235    }
236}
237
238impl TryIntoRequest for CreateForumThreadMessage<'_> {
239    fn try_into_request(self) -> Result<crate::request::Request, Error> {
240        self.0
241            .map_err(Error::validation)
242            .and_then(CreateForumThread::try_into_request)
243    }
244}