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

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