1mod action_row;
6mod checkbox;
7mod checkbox_group;
8mod file_upload;
9mod label;
10mod select_menu;
11mod text_display;
12mod text_input;
13
14pub use self::{
15 action_row::ModalInteractionActionRow,
16 checkbox::ModalInteractionCheckbox,
17 checkbox_group::ModalInteractionCheckboxGroup,
18 file_upload::ModalInteractionFileUpload,
19 label::ModalInteractionLabel,
20 select_menu::{
21 ModalInteractionChannelSelect, ModalInteractionMentionableSelect,
22 ModalInteractionRoleSelect, ModalInteractionStringSelect, ModalInteractionUserSelect,
23 },
24 text_display::ModalInteractionTextDisplay,
25 text_input::ModalInteractionTextInput,
26};
27use crate::application::interaction::InteractionDataResolved;
28use crate::application::interaction::modal::select_menu::ModalInteractionSelectMenu;
29use crate::channel::message::component::ComponentType;
30use crate::id::Id;
31use crate::id::marker::{ChannelMarker, GenericMarker, RoleMarker, UserMarker};
32use serde::{
33 Deserialize, Serialize, Serializer,
34 de::{Deserializer, Error as DeError, IgnoredAny, MapAccess, Visitor},
35 ser::SerializeStruct,
36};
37use serde_value::{DeserializerError, Value};
38use std::fmt::Formatter;
39
40#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
47pub struct ModalInteractionData {
48 pub components: Vec<ModalInteractionComponent>,
50 pub custom_id: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub resolved: Option<InteractionDataResolved>,
59}
60
61#[derive(Clone, Debug, Eq, PartialEq)]
63pub enum ModalInteractionComponent {
64 ActionRow(ModalInteractionActionRow),
66 ChannelSelect(ModalInteractionChannelSelect),
68 Checkbox(ModalInteractionCheckbox),
70 CheckboxGroup(ModalInteractionCheckboxGroup),
72 FileUpload(ModalInteractionFileUpload),
74 Label(ModalInteractionLabel),
76 MentionableSelect(ModalInteractionMentionableSelect),
78 RoleSelect(ModalInteractionRoleSelect),
80 StringSelect(ModalInteractionStringSelect),
82 TextDisplay(ModalInteractionTextDisplay),
84 TextInput(ModalInteractionTextInput),
86 Unknown(u8),
88 UserSelect(ModalInteractionUserSelect),
90}
91
92impl ModalInteractionComponent {
93 pub fn kind(&self) -> ComponentType {
95 match self {
96 ModalInteractionComponent::ActionRow(_) => ComponentType::ActionRow,
97 ModalInteractionComponent::ChannelSelect(_) => ComponentType::ChannelSelectMenu,
98 ModalInteractionComponent::Checkbox(_) => ComponentType::Checkbox,
99 ModalInteractionComponent::CheckboxGroup(_) => ComponentType::CheckboxGroup,
100 ModalInteractionComponent::FileUpload(_) => ComponentType::FileUpload,
101 ModalInteractionComponent::Label(_) => ComponentType::Label,
102 ModalInteractionComponent::MentionableSelect(_) => ComponentType::MentionableSelectMenu,
103 ModalInteractionComponent::RoleSelect(_) => ComponentType::RoleSelectMenu,
104 ModalInteractionComponent::StringSelect(_) => ComponentType::TextSelectMenu,
105 ModalInteractionComponent::TextDisplay(_) => ComponentType::TextDisplay,
106 ModalInteractionComponent::TextInput(_) => ComponentType::TextInput,
107 ModalInteractionComponent::Unknown(unknown) => ComponentType::from(*unknown),
108 ModalInteractionComponent::UserSelect(_) => ComponentType::UserSelectMenu,
109 }
110 }
111}
112
113impl From<ModalInteractionActionRow> for ModalInteractionComponent {
114 fn from(action_row: ModalInteractionActionRow) -> Self {
115 Self::ActionRow(action_row)
116 }
117}
118
119impl From<ModalInteractionChannelSelect> for ModalInteractionComponent {
120 fn from(select: ModalInteractionChannelSelect) -> Self {
121 Self::ChannelSelect(select)
122 }
123}
124
125impl From<ModalInteractionCheckbox> for ModalInteractionComponent {
126 fn from(checkbox: ModalInteractionCheckbox) -> Self {
127 Self::Checkbox(checkbox)
128 }
129}
130
131impl From<ModalInteractionCheckboxGroup> for ModalInteractionComponent {
132 fn from(checkbox_group: ModalInteractionCheckboxGroup) -> Self {
133 Self::CheckboxGroup(checkbox_group)
134 }
135}
136
137impl From<ModalInteractionFileUpload> for ModalInteractionComponent {
138 fn from(file_upload: ModalInteractionFileUpload) -> Self {
139 Self::FileUpload(file_upload)
140 }
141}
142
143impl From<ModalInteractionLabel> for ModalInteractionComponent {
144 fn from(label: ModalInteractionLabel) -> Self {
145 Self::Label(label)
146 }
147}
148
149impl From<ModalInteractionMentionableSelect> for ModalInteractionComponent {
150 fn from(select: ModalInteractionMentionableSelect) -> Self {
151 Self::MentionableSelect(select)
152 }
153}
154
155impl From<ModalInteractionRoleSelect> for ModalInteractionComponent {
156 fn from(select: ModalInteractionRoleSelect) -> Self {
157 Self::RoleSelect(select)
158 }
159}
160
161impl From<ModalInteractionStringSelect> for ModalInteractionComponent {
162 fn from(select: ModalInteractionStringSelect) -> Self {
163 Self::StringSelect(select)
164 }
165}
166
167impl From<ModalInteractionTextDisplay> for ModalInteractionComponent {
168 fn from(text_display: ModalInteractionTextDisplay) -> Self {
169 Self::TextDisplay(text_display)
170 }
171}
172
173impl From<ModalInteractionTextInput> for ModalInteractionComponent {
174 fn from(text_input: ModalInteractionTextInput) -> Self {
175 Self::TextInput(text_input)
176 }
177}
178
179impl From<ModalInteractionUserSelect> for ModalInteractionComponent {
180 fn from(select: ModalInteractionUserSelect) -> Self {
181 Self::UserSelect(select)
182 }
183}
184
185impl TryFrom<ModalInteractionComponent> for ModalInteractionActionRow {
186 type Error = ModalInteractionComponent;
187
188 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
189 match value {
190 ModalInteractionComponent::ActionRow(inner) => Ok(inner),
191 _ => Err(value),
192 }
193 }
194}
195
196impl TryFrom<ModalInteractionComponent> for ModalInteractionChannelSelect {
197 type Error = ModalInteractionComponent;
198
199 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
200 match value {
201 ModalInteractionComponent::ChannelSelect(inner) => Ok(inner),
202 _ => Err(value),
203 }
204 }
205}
206
207impl TryFrom<ModalInteractionComponent> for ModalInteractionCheckbox {
208 type Error = ModalInteractionComponent;
209
210 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
211 match value {
212 ModalInteractionComponent::Checkbox(inner) => Ok(inner),
213 _ => Err(value),
214 }
215 }
216}
217
218impl TryFrom<ModalInteractionComponent> for ModalInteractionCheckboxGroup {
219 type Error = ModalInteractionComponent;
220
221 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
222 match value {
223 ModalInteractionComponent::CheckboxGroup(inner) => Ok(inner),
224 _ => Err(value),
225 }
226 }
227}
228
229impl TryFrom<ModalInteractionComponent> for ModalInteractionFileUpload {
230 type Error = ModalInteractionComponent;
231
232 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
233 match value {
234 ModalInteractionComponent::FileUpload(inner) => Ok(inner),
235 _ => Err(value),
236 }
237 }
238}
239
240impl TryFrom<ModalInteractionComponent> for ModalInteractionLabel {
241 type Error = ModalInteractionComponent;
242
243 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
244 match value {
245 ModalInteractionComponent::Label(inner) => Ok(inner),
246 _ => Err(value),
247 }
248 }
249}
250
251impl TryFrom<ModalInteractionComponent> for ModalInteractionMentionableSelect {
252 type Error = ModalInteractionComponent;
253
254 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
255 match value {
256 ModalInteractionComponent::MentionableSelect(inner) => Ok(inner),
257 _ => Err(value),
258 }
259 }
260}
261
262impl TryFrom<ModalInteractionComponent> for ModalInteractionRoleSelect {
263 type Error = ModalInteractionComponent;
264
265 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
266 match value {
267 ModalInteractionComponent::RoleSelect(inner) => Ok(inner),
268 _ => Err(value),
269 }
270 }
271}
272
273impl TryFrom<ModalInteractionComponent> for ModalInteractionStringSelect {
274 type Error = ModalInteractionComponent;
275
276 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
277 match value {
278 ModalInteractionComponent::StringSelect(inner) => Ok(inner),
279 _ => Err(value),
280 }
281 }
282}
283
284impl TryFrom<ModalInteractionComponent> for ModalInteractionTextDisplay {
285 type Error = ModalInteractionComponent;
286
287 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
288 match value {
289 ModalInteractionComponent::TextDisplay(inner) => Ok(inner),
290 _ => Err(value),
291 }
292 }
293}
294
295impl TryFrom<ModalInteractionComponent> for ModalInteractionTextInput {
296 type Error = ModalInteractionComponent;
297
298 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
299 match value {
300 ModalInteractionComponent::TextInput(inner) => Ok(inner),
301 _ => Err(value),
302 }
303 }
304}
305
306impl TryFrom<ModalInteractionComponent> for ModalInteractionUserSelect {
307 type Error = ModalInteractionComponent;
308
309 fn try_from(value: ModalInteractionComponent) -> Result<Self, Self::Error> {
310 match value {
311 ModalInteractionComponent::UserSelect(inner) => Ok(inner),
312 _ => Err(value),
313 }
314 }
315}
316
317impl<'de> Deserialize<'de> for ModalInteractionComponent {
318 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319 where
320 D: Deserializer<'de>,
321 {
322 deserializer.deserialize_any(ModalInteractionDataComponentVisitor)
323 }
324}
325
326#[derive(Debug, Deserialize)]
327#[serde(field_identifier, rename_all = "snake_case")]
328enum Field {
329 Component,
330 Components,
331 CustomId,
332 Id,
333 Type,
334 Value,
335 Values,
336}
337
338struct ModalInteractionDataComponentVisitor;
339
340impl<'de> Visitor<'de> for ModalInteractionDataComponentVisitor {
341 type Value = ModalInteractionComponent;
342
343 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
344 f.write_str("struct ModalInteractionDataComponent")
345 }
346
347 #[allow(clippy::too_many_lines)]
348 fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
349 fn deserialize_select_menu<'de, ValueType: Deserialize<'de>, Error: DeError>(
350 id: i32,
351 custom_id: Option<String>,
352 values: Option<Vec<Value>>,
353 ) -> Result<ModalInteractionSelectMenu<ValueType>, Error> {
354 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
355 let values = values
356 .ok_or_else(|| DeError::missing_field("values"))?
357 .into_iter()
358 .map(Value::deserialize_into)
359 .collect::<Result<_, _>>()
360 .map_err(DeserializerError::into_error)?;
361
362 Ok(ModalInteractionSelectMenu {
363 id,
364 custom_id,
365 values,
366 })
367 }
368
369 let mut id: Option<i32> = None;
371 let mut kind: Option<ComponentType> = None;
372 let mut custom_id: Option<String> = None;
373 let mut values: Option<Vec<Value>> = None;
374 let mut components: Option<Vec<ModalInteractionComponent>> = None;
375 let mut component: Option<ModalInteractionComponent> = None;
376 let mut value: Option<String> = None;
377
378 loop {
379 let key = match map.next_key() {
380 Ok(Some(key)) => key,
381 Ok(None) => break,
382 Err(_) => {
383 map.next_value::<IgnoredAny>()?;
384
385 continue;
386 }
387 };
388
389 match key {
390 Field::Component => {
391 if component.is_some() {
392 return Err(DeError::duplicate_field("component"));
393 }
394
395 component = Some(map.next_value()?);
396 }
397 Field::Components => {
398 if components.is_some() {
399 return Err(DeError::duplicate_field("components"));
400 }
401
402 components = Some(map.next_value()?);
403 }
404 Field::CustomId => {
405 if custom_id.is_some() {
406 return Err(DeError::duplicate_field("custom_id"));
407 }
408
409 custom_id = Some(map.next_value()?);
410 }
411 Field::Id => {
412 if id.is_some() {
413 return Err(DeError::duplicate_field("id"));
414 }
415
416 id = Some(map.next_value()?);
417 }
418 Field::Type => {
419 if kind.is_some() {
420 return Err(DeError::duplicate_field("kind"));
421 }
422
423 kind = Some(map.next_value()?);
424 }
425 Field::Value => {
426 if value.is_some() {
427 return Err(DeError::duplicate_field("value"));
428 }
429
430 value = Some(map.next_value()?);
431 }
432 Field::Values => {
433 if values.is_some() {
434 return Err(DeError::duplicate_field("values"));
435 }
436
437 values = Some(map.next_value()?);
438 }
439 }
440 }
441
442 let kind = kind.ok_or_else(|| DeError::missing_field("type"))?;
443 let id = id.ok_or_else(|| DeError::missing_field("id"))?;
444
445 Ok(match kind {
446 ComponentType::ActionRow => {
447 let components = components.ok_or_else(|| DeError::missing_field("components"))?;
448
449 Self::Value::ActionRow(ModalInteractionActionRow { components, id })
450 }
451 ComponentType::TextSelectMenu => {
452 Self::Value::StringSelect(deserialize_select_menu::<String, _>(
453 id, custom_id, values,
454 )?)
455 }
456 ComponentType::UserSelectMenu => {
457 Self::Value::UserSelect(deserialize_select_menu::<Id<UserMarker>, _>(
458 id, custom_id, values,
459 )?)
460 }
461 ComponentType::RoleSelectMenu => {
462 Self::Value::RoleSelect(deserialize_select_menu::<Id<RoleMarker>, _>(
463 id, custom_id, values,
464 )?)
465 }
466 ComponentType::MentionableSelectMenu => Self::Value::MentionableSelect(
467 deserialize_select_menu::<Id<GenericMarker>, _>(id, custom_id, values)?,
468 ),
469 ComponentType::ChannelSelectMenu => Self::Value::ChannelSelect(
470 deserialize_select_menu::<Id<ChannelMarker>, _>(id, custom_id, values)?,
471 ),
472 ComponentType::TextInput => {
473 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
474 let value = value.ok_or_else(|| DeError::missing_field("value"))?;
475
476 Self::Value::TextInput(ModalInteractionTextInput {
477 custom_id,
478 id,
479 value,
480 })
481 }
482 ComponentType::TextDisplay => {
483 Self::Value::TextDisplay(ModalInteractionTextDisplay { id })
484 }
485 ComponentType::Label => {
486 let component = component.ok_or_else(|| DeError::missing_field("component"))?;
487
488 Self::Value::Label(ModalInteractionLabel {
489 id,
490 component: Box::new(component),
491 })
492 }
493 ComponentType::FileUpload => {
494 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
495 let values = values
496 .ok_or_else(|| DeError::missing_field("values"))?
497 .into_iter()
498 .map(Value::deserialize_into)
499 .collect::<Result<_, _>>()
500 .map_err(DeserializerError::into_error)?;
501
502 Self::Value::FileUpload(ModalInteractionFileUpload {
503 custom_id,
504 id,
505 values,
506 })
507 }
508 ComponentType::CheckboxGroup => {
509 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
510 let values = values
511 .ok_or_else(|| DeError::missing_field("values"))?
512 .into_iter()
513 .map(Value::deserialize_into)
514 .collect::<Result<_, _>>()
515 .map_err(DeserializerError::into_error)?;
516
517 Self::Value::CheckboxGroup(ModalInteractionCheckboxGroup {
518 custom_id,
519 id,
520 values,
521 })
522 }
523 ComponentType::Checkbox => {
524 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
525 let value = value.ok_or_else(|| DeError::missing_field("value"))?;
526
527 Self::Value::TextInput(ModalInteractionTextInput {
528 custom_id,
529 id,
530 value,
531 })
532 }
533 ComponentType::Button
534 | ComponentType::Section
535 | ComponentType::Thumbnail
536 | ComponentType::MediaGallery
537 | ComponentType::File
538 | ComponentType::Separator
539 | ComponentType::Container
540 | ComponentType::Unknown(_) => Self::Value::Unknown(kind.into()),
541 })
542 }
543}
544
545impl Serialize for ModalInteractionComponent {
546 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
547 fn serialize_select_menu<State: SerializeStruct, ValueType: Serialize>(
548 state: &mut State,
549 select: &ModalInteractionSelectMenu<ValueType>,
550 ) -> Result<(), <State as SerializeStruct>::Error> {
551 state.serialize_field("custom_id", &select.custom_id)?;
552 state.serialize_field("id", &select.id)?;
553 state.serialize_field("values", &select.values)?;
554 Ok(())
555 }
556
557 #[allow(clippy::match_same_arms)]
558 let len = match self {
559 ModalInteractionComponent::ActionRow(_) => 3,
564 ModalInteractionComponent::ChannelSelect(_)
570 | ModalInteractionComponent::MentionableSelect(_)
571 | ModalInteractionComponent::RoleSelect(_)
572 | ModalInteractionComponent::UserSelect(_)
573 | ModalInteractionComponent::StringSelect(_) => 4,
574 ModalInteractionComponent::Checkbox(_) => 4,
580 ModalInteractionComponent::CheckboxGroup(_) => 4,
586 ModalInteractionComponent::FileUpload(_) => 4,
592 ModalInteractionComponent::Label(_) => 3,
597 ModalInteractionComponent::TextDisplay(_) => 2,
601 ModalInteractionComponent::TextInput(_) => 4,
607 ModalInteractionComponent::Unknown(_) => 1,
610 };
611
612 let mut state = serializer.serialize_struct("ModalInteractionComponent", len)?;
613 state.serialize_field("type", &self.kind())?;
614
615 match self {
616 ModalInteractionComponent::ActionRow(action_row) => {
617 state.serialize_field("id", &action_row.id)?;
618 state.serialize_field("components", &action_row.components)?;
619 }
620 ModalInteractionComponent::ChannelSelect(select) => {
621 serialize_select_menu(&mut state, select)?;
622 }
623 ModalInteractionComponent::Checkbox(checkbox) => {
624 state.serialize_field("custom_id", &checkbox.custom_id)?;
625 state.serialize_field("id", &checkbox.id)?;
626 state.serialize_field("value", &checkbox.value)?;
627 }
628 ModalInteractionComponent::CheckboxGroup(checkbox_group) => {
629 state.serialize_field("custom_id", &checkbox_group.custom_id)?;
630 state.serialize_field("id", &checkbox_group.id)?;
631 state.serialize_field("values", &checkbox_group.values)?;
632 }
633 ModalInteractionComponent::FileUpload(file_upload) => {
634 state.serialize_field("custom_id", &file_upload.custom_id)?;
635 state.serialize_field("id", &file_upload.id)?;
636 state.serialize_field("values", &file_upload.values)?;
637 }
638 ModalInteractionComponent::Label(label) => {
639 state.serialize_field("id", &label.id)?;
640 state.serialize_field("component", &label.component)?;
641 }
642 ModalInteractionComponent::MentionableSelect(select) => {
643 serialize_select_menu(&mut state, select)?;
644 }
645 ModalInteractionComponent::RoleSelect(select) => {
646 serialize_select_menu(&mut state, select)?;
647 }
648 ModalInteractionComponent::StringSelect(select) => {
649 serialize_select_menu(&mut state, select)?;
650 }
651 ModalInteractionComponent::TextDisplay(text_display) => {
652 state.serialize_field("id", &text_display.id)?;
653 }
654 ModalInteractionComponent::TextInput(text_input) => {
655 state.serialize_field("custom_id", &text_input.custom_id)?;
656 state.serialize_field("id", &text_input.id)?;
657 state.serialize_field("value", &text_input.value)?;
658 }
659 ModalInteractionComponent::Unknown(_) => {}
663 ModalInteractionComponent::UserSelect(select) => {
664 serialize_select_menu(&mut state, select)?;
665 }
666 }
667
668 state.end()
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675 use crate::application::interaction::InteractionChannel;
676 use crate::channel::ChannelType;
677 use crate::guild::Permissions;
678 use serde_test::Token;
679 use static_assertions::{assert_fields, assert_impl_all};
680 use std::collections::HashMap;
681 use std::fmt::Debug;
682
683 assert_fields!(ModalInteractionData: custom_id, components);
684 assert_impl_all!(
685 ModalInteractionData: Clone,
686 Debug,
687 Deserialize<'static>,
688 PartialEq,
689 Send,
690 Serialize,
691 Sync
692 );
693
694 assert_impl_all!(
695 ModalInteractionComponent: Clone,
696 Debug,
697 Deserialize<'static>,
698 Eq,
699 PartialEq,
700 Send,
701 Serialize,
702 Sync
703 );
704
705 fn label_tokens(id: i32, component_tokens: &[Token]) -> Vec<Token> {
706 let mut label_tokens = vec![
707 Token::Struct {
708 name: "ModalInteractionComponent",
709 len: 3,
710 },
711 Token::String("type"),
712 Token::U8(ComponentType::Label.into()),
713 Token::String("id"),
714 Token::I32(id),
715 Token::String("component"),
716 ];
717 label_tokens.extend_from_slice(component_tokens);
718 label_tokens.push(Token::StructEnd);
719
720 label_tokens
721 }
722
723 #[test]
724 fn modal_action_rows() {
725 let value = ModalInteractionData {
726 custom_id: "test-modal".to_owned(),
727 components: vec![ModalInteractionComponent::ActionRow(
728 ModalInteractionActionRow {
729 id: 1,
730 components: vec![ModalInteractionComponent::TextInput(
731 ModalInteractionTextInput {
732 custom_id: "the-data-id".to_owned(),
733 id: 2,
734 value: "input value".to_owned(),
735 },
736 )],
737 },
738 )],
739 resolved: None,
740 };
741
742 serde_test::assert_tokens(
743 &value,
744 &[
745 Token::Struct {
746 name: "ModalInteractionData",
747 len: 2,
748 },
749 Token::String("components"),
750 Token::Seq { len: Some(1) },
751 Token::Struct {
752 name: "ModalInteractionComponent",
753 len: 3,
754 },
755 Token::String("type"),
756 Token::U8(ComponentType::ActionRow.into()),
757 Token::String("id"),
758 Token::I32(1),
759 Token::String("components"),
760 Token::Seq { len: Some(1) },
761 Token::Struct {
762 name: "ModalInteractionComponent",
763 len: 4,
764 },
765 Token::String("type"),
766 Token::U8(ComponentType::TextInput.into()),
767 Token::String("custom_id"),
768 Token::String("the-data-id"),
769 Token::String("id"),
770 Token::I32(2),
771 Token::String("value"),
772 Token::String("input value"),
773 Token::StructEnd,
774 Token::SeqEnd,
775 Token::StructEnd,
776 Token::SeqEnd,
777 Token::String("custom_id"),
778 Token::String("test-modal"),
779 Token::StructEnd,
780 ],
781 );
782 }
783
784 #[test]
785 #[allow(clippy::too_many_lines)]
786 fn modal_labels() {
787 let value = ModalInteractionData {
788 custom_id: "test-modal".to_owned(),
789 components: vec![
790 ModalInteractionComponent::Label(ModalInteractionLabel {
791 id: 1,
792 component: Box::new(ModalInteractionComponent::TextInput(
793 ModalInteractionTextInput {
794 custom_id: "the-text-input-id".to_owned(),
795 id: 2,
796 value: "input value".to_owned(),
797 },
798 )),
799 }),
800 ModalInteractionComponent::Label(ModalInteractionLabel {
801 id: 3,
802 component: Box::new(ModalInteractionComponent::TextDisplay(
803 ModalInteractionTextDisplay { id: 4 },
804 )),
805 }),
806 ModalInteractionComponent::Label(ModalInteractionLabel {
807 id: 5,
808 component: Box::new(ModalInteractionComponent::ChannelSelect(
809 ModalInteractionChannelSelect {
810 id: 6,
811 custom_id: "the-channel-select-id".to_owned(),
812 values: vec![Id::new(42)],
813 },
814 )),
815 }),
816 ],
817 resolved: Some(InteractionDataResolved {
818 attachments: HashMap::new(),
819 channels: HashMap::from([(
820 Id::new(42),
821 InteractionChannel {
822 id: Id::new(42),
823 kind: ChannelType::GuildText,
824 name: "the-channel-name".to_owned(),
825 parent_id: None,
826 permissions: Permissions::empty(),
827 thread_metadata: None,
828 },
829 )]),
830 members: HashMap::new(),
831 messages: HashMap::new(),
832 roles: HashMap::new(),
833 users: HashMap::new(),
834 }),
835 };
836
837 let text_input_tokens = [
838 Token::Struct {
839 name: "ModalInteractionComponent",
840 len: 4,
841 },
842 Token::String("type"),
843 Token::U8(ComponentType::TextInput.into()),
844 Token::String("custom_id"),
845 Token::String("the-text-input-id"),
846 Token::String("id"),
847 Token::I32(2),
848 Token::String("value"),
849 Token::String("input value"),
850 Token::StructEnd,
851 ];
852
853 let text_display_tokens = [
854 Token::Struct {
855 name: "ModalInteractionComponent",
856 len: 2,
857 },
858 Token::String("type"),
859 Token::U8(ComponentType::TextDisplay.into()),
860 Token::String("id"),
861 Token::I32(4),
862 Token::StructEnd,
863 ];
864
865 let channel_select_tokens = [
866 Token::Struct {
867 name: "ModalInteractionComponent",
868 len: 4,
869 },
870 Token::String("type"),
871 Token::U8(ComponentType::ChannelSelectMenu.into()),
872 Token::String("custom_id"),
873 Token::String("the-channel-select-id"),
874 Token::String("id"),
875 Token::I32(6),
876 Token::String("values"),
877 Token::Seq { len: Some(1) },
878 Token::NewtypeStruct { name: "Id" },
879 Token::String("42"),
880 Token::SeqEnd,
881 Token::StructEnd,
882 ];
883
884 let mut all_tokens = vec![
885 Token::Struct {
886 name: "ModalInteractionData",
887 len: 3,
888 },
889 Token::String("components"),
890 Token::Seq { len: Some(3) },
891 ];
892
893 all_tokens.extend_from_slice(&label_tokens(1, &text_input_tokens));
894 all_tokens.extend_from_slice(&label_tokens(3, &text_display_tokens));
895 all_tokens.extend_from_slice(&label_tokens(5, &channel_select_tokens));
896
897 all_tokens.extend_from_slice(&[
898 Token::SeqEnd,
899 Token::String("custom_id"),
900 Token::String("test-modal"),
901 Token::String("resolved"),
902 Token::Some,
903 Token::Struct {
904 name: "InteractionDataResolved",
905 len: 1,
906 },
907 Token::String("channels"),
908 Token::Map { len: Some(1) },
909 Token::NewtypeStruct { name: "Id" },
910 Token::String("42"),
911 Token::Struct {
912 name: "InteractionChannel",
913 len: 4,
914 },
915 Token::String("id"),
916 Token::NewtypeStruct { name: "Id" },
917 Token::String("42"),
918 Token::String("type"),
919 Token::U8(0),
920 Token::String("name"),
921 Token::String("the-channel-name"),
922 Token::String("permissions"),
923 Token::String("0"),
924 Token::StructEnd,
925 Token::MapEnd,
926 Token::StructEnd,
927 Token::StructEnd,
928 ]);
929
930 serde_test::assert_tokens(&value, &all_tokens);
931 }
932
933 #[test]
934 fn modal_file_upload() {
935 let value = ModalInteractionData {
936 custom_id: "test-modal".to_owned(),
937 components: vec![ModalInteractionComponent::FileUpload(
938 ModalInteractionFileUpload {
939 id: 42,
940 custom_id: "file-upload".to_owned(),
941 values: vec![Id::new(1), Id::new(2)],
942 },
943 )],
944 resolved: None,
947 };
948
949 serde_test::assert_tokens(
950 &value,
951 &[
952 Token::Struct {
953 name: "ModalInteractionData",
954 len: 2,
955 },
956 Token::String("components"),
957 Token::Seq { len: Some(1) },
958 Token::Struct {
959 name: "ModalInteractionComponent",
960 len: 4,
961 },
962 Token::String("type"),
963 Token::U8(ComponentType::FileUpload.into()),
964 Token::String("custom_id"),
965 Token::String("file-upload"),
966 Token::String("id"),
967 Token::I32(42),
968 Token::String("values"),
969 Token::Seq { len: Some(2) },
970 Token::NewtypeStruct { name: "Id" },
971 Token::Str("1"),
972 Token::NewtypeStruct { name: "Id" },
973 Token::Str("2"),
974 Token::SeqEnd,
975 Token::StructEnd,
976 Token::SeqEnd,
977 Token::String("custom_id"),
978 Token::String("test-modal"),
979 Token::StructEnd,
980 ],
981 )
982 }
983}