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<Value> = 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
475 .ok_or_else(|| DeError::missing_field("value"))?
476 .deserialize_into()
477 .map_err(DeserializerError::into_error)?;
478
479 Self::Value::TextInput(ModalInteractionTextInput {
480 custom_id,
481 id,
482 value,
483 })
484 }
485 ComponentType::TextDisplay => {
486 Self::Value::TextDisplay(ModalInteractionTextDisplay { id })
487 }
488 ComponentType::Label => {
489 let component = component.ok_or_else(|| DeError::missing_field("component"))?;
490
491 Self::Value::Label(ModalInteractionLabel {
492 id,
493 component: Box::new(component),
494 })
495 }
496 ComponentType::FileUpload => {
497 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
498 let values = values
499 .ok_or_else(|| DeError::missing_field("values"))?
500 .into_iter()
501 .map(Value::deserialize_into)
502 .collect::<Result<_, _>>()
503 .map_err(DeserializerError::into_error)?;
504
505 Self::Value::FileUpload(ModalInteractionFileUpload {
506 custom_id,
507 id,
508 values,
509 })
510 }
511 ComponentType::CheckboxGroup => {
512 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
513 let values = values
514 .ok_or_else(|| DeError::missing_field("values"))?
515 .into_iter()
516 .map(Value::deserialize_into)
517 .collect::<Result<_, _>>()
518 .map_err(DeserializerError::into_error)?;
519
520 Self::Value::CheckboxGroup(ModalInteractionCheckboxGroup {
521 custom_id,
522 id,
523 values,
524 })
525 }
526 ComponentType::Checkbox => {
527 let custom_id = custom_id.ok_or_else(|| DeError::missing_field("custom_id"))?;
528 let value = value
529 .ok_or_else(|| DeError::missing_field("value"))?
530 .deserialize_into()
531 .map_err(DeserializerError::into_error)?;
532
533 Self::Value::Checkbox(ModalInteractionCheckbox {
534 custom_id,
535 id,
536 value,
537 })
538 }
539 ComponentType::Button
540 | ComponentType::Section
541 | ComponentType::Thumbnail
542 | ComponentType::MediaGallery
543 | ComponentType::File
544 | ComponentType::Separator
545 | ComponentType::Container
546 | ComponentType::Unknown(_) => Self::Value::Unknown(kind.into()),
547 })
548 }
549}
550
551impl Serialize for ModalInteractionComponent {
552 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
553 fn serialize_select_menu<State: SerializeStruct, ValueType: Serialize>(
554 state: &mut State,
555 select: &ModalInteractionSelectMenu<ValueType>,
556 ) -> Result<(), <State as SerializeStruct>::Error> {
557 state.serialize_field("custom_id", &select.custom_id)?;
558 state.serialize_field("id", &select.id)?;
559 state.serialize_field("values", &select.values)?;
560 Ok(())
561 }
562
563 #[allow(clippy::match_same_arms)]
564 let len = match self {
565 ModalInteractionComponent::ActionRow(_) => 3,
570 ModalInteractionComponent::ChannelSelect(_)
576 | ModalInteractionComponent::MentionableSelect(_)
577 | ModalInteractionComponent::RoleSelect(_)
578 | ModalInteractionComponent::UserSelect(_)
579 | ModalInteractionComponent::StringSelect(_) => 4,
580 ModalInteractionComponent::Checkbox(_) => 4,
586 ModalInteractionComponent::CheckboxGroup(_) => 4,
592 ModalInteractionComponent::FileUpload(_) => 4,
598 ModalInteractionComponent::Label(_) => 3,
603 ModalInteractionComponent::TextDisplay(_) => 2,
607 ModalInteractionComponent::TextInput(_) => 4,
613 ModalInteractionComponent::Unknown(_) => 1,
616 };
617
618 let mut state = serializer.serialize_struct("ModalInteractionComponent", len)?;
619 state.serialize_field("type", &self.kind())?;
620
621 match self {
622 ModalInteractionComponent::ActionRow(action_row) => {
623 state.serialize_field("id", &action_row.id)?;
624 state.serialize_field("components", &action_row.components)?;
625 }
626 ModalInteractionComponent::ChannelSelect(select) => {
627 serialize_select_menu(&mut state, select)?;
628 }
629 ModalInteractionComponent::Checkbox(checkbox) => {
630 state.serialize_field("custom_id", &checkbox.custom_id)?;
631 state.serialize_field("id", &checkbox.id)?;
632 state.serialize_field("value", &checkbox.value)?;
633 }
634 ModalInteractionComponent::CheckboxGroup(checkbox_group) => {
635 state.serialize_field("custom_id", &checkbox_group.custom_id)?;
636 state.serialize_field("id", &checkbox_group.id)?;
637 state.serialize_field("values", &checkbox_group.values)?;
638 }
639 ModalInteractionComponent::FileUpload(file_upload) => {
640 state.serialize_field("custom_id", &file_upload.custom_id)?;
641 state.serialize_field("id", &file_upload.id)?;
642 state.serialize_field("values", &file_upload.values)?;
643 }
644 ModalInteractionComponent::Label(label) => {
645 state.serialize_field("id", &label.id)?;
646 state.serialize_field("component", &label.component)?;
647 }
648 ModalInteractionComponent::MentionableSelect(select) => {
649 serialize_select_menu(&mut state, select)?;
650 }
651 ModalInteractionComponent::RoleSelect(select) => {
652 serialize_select_menu(&mut state, select)?;
653 }
654 ModalInteractionComponent::StringSelect(select) => {
655 serialize_select_menu(&mut state, select)?;
656 }
657 ModalInteractionComponent::TextDisplay(text_display) => {
658 state.serialize_field("id", &text_display.id)?;
659 }
660 ModalInteractionComponent::TextInput(text_input) => {
661 state.serialize_field("custom_id", &text_input.custom_id)?;
662 state.serialize_field("id", &text_input.id)?;
663 state.serialize_field("value", &text_input.value)?;
664 }
665 ModalInteractionComponent::Unknown(_) => {}
669 ModalInteractionComponent::UserSelect(select) => {
670 serialize_select_menu(&mut state, select)?;
671 }
672 }
673
674 state.end()
675 }
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681 use crate::application::interaction::InteractionChannel;
682 use crate::channel::ChannelType;
683 use crate::guild::Permissions;
684 use serde_test::Token;
685 use static_assertions::{assert_fields, assert_impl_all};
686 use std::collections::HashMap;
687 use std::fmt::Debug;
688
689 assert_fields!(ModalInteractionData: custom_id, components);
690 assert_impl_all!(
691 ModalInteractionData: Clone,
692 Debug,
693 Deserialize<'static>,
694 PartialEq,
695 Send,
696 Serialize,
697 Sync
698 );
699
700 assert_impl_all!(
701 ModalInteractionComponent: Clone,
702 Debug,
703 Deserialize<'static>,
704 Eq,
705 PartialEq,
706 Send,
707 Serialize,
708 Sync
709 );
710
711 fn label_tokens(id: i32, component_tokens: &[Token]) -> Vec<Token> {
712 let mut label_tokens = vec![
713 Token::Struct {
714 name: "ModalInteractionComponent",
715 len: 3,
716 },
717 Token::String("type"),
718 Token::U8(ComponentType::Label.into()),
719 Token::String("id"),
720 Token::I32(id),
721 Token::String("component"),
722 ];
723 label_tokens.extend_from_slice(component_tokens);
724 label_tokens.push(Token::StructEnd);
725
726 label_tokens
727 }
728
729 #[test]
730 fn modal_action_rows() {
731 let value = ModalInteractionData {
732 custom_id: "test-modal".to_owned(),
733 components: vec![ModalInteractionComponent::ActionRow(
734 ModalInteractionActionRow {
735 id: 1,
736 components: vec![ModalInteractionComponent::TextInput(
737 ModalInteractionTextInput {
738 custom_id: "the-data-id".to_owned(),
739 id: 2,
740 value: "input value".to_owned(),
741 },
742 )],
743 },
744 )],
745 resolved: None,
746 };
747
748 serde_test::assert_tokens(
749 &value,
750 &[
751 Token::Struct {
752 name: "ModalInteractionData",
753 len: 2,
754 },
755 Token::String("components"),
756 Token::Seq { len: Some(1) },
757 Token::Struct {
758 name: "ModalInteractionComponent",
759 len: 3,
760 },
761 Token::String("type"),
762 Token::U8(ComponentType::ActionRow.into()),
763 Token::String("id"),
764 Token::I32(1),
765 Token::String("components"),
766 Token::Seq { len: Some(1) },
767 Token::Struct {
768 name: "ModalInteractionComponent",
769 len: 4,
770 },
771 Token::String("type"),
772 Token::U8(ComponentType::TextInput.into()),
773 Token::String("custom_id"),
774 Token::String("the-data-id"),
775 Token::String("id"),
776 Token::I32(2),
777 Token::String("value"),
778 Token::String("input value"),
779 Token::StructEnd,
780 Token::SeqEnd,
781 Token::StructEnd,
782 Token::SeqEnd,
783 Token::String("custom_id"),
784 Token::String("test-modal"),
785 Token::StructEnd,
786 ],
787 );
788 }
789
790 #[test]
791 #[allow(clippy::too_many_lines)]
792 fn modal_labels() {
793 let value = ModalInteractionData {
794 custom_id: "test-modal".to_owned(),
795 components: vec![
796 ModalInteractionComponent::Label(ModalInteractionLabel {
797 id: 1,
798 component: Box::new(ModalInteractionComponent::TextInput(
799 ModalInteractionTextInput {
800 custom_id: "the-text-input-id".to_owned(),
801 id: 2,
802 value: "input value".to_owned(),
803 },
804 )),
805 }),
806 ModalInteractionComponent::Label(ModalInteractionLabel {
807 id: 3,
808 component: Box::new(ModalInteractionComponent::TextDisplay(
809 ModalInteractionTextDisplay { id: 4 },
810 )),
811 }),
812 ModalInteractionComponent::Label(ModalInteractionLabel {
813 id: 5,
814 component: Box::new(ModalInteractionComponent::ChannelSelect(
815 ModalInteractionChannelSelect {
816 id: 6,
817 custom_id: "the-channel-select-id".to_owned(),
818 values: vec![Id::new(42)],
819 },
820 )),
821 }),
822 ],
823 resolved: Some(InteractionDataResolved {
824 attachments: HashMap::new(),
825 channels: HashMap::from([(
826 Id::new(42),
827 InteractionChannel {
828 id: Id::new(42),
829 kind: ChannelType::GuildText,
830 name: "the-channel-name".to_owned(),
831 parent_id: None,
832 permissions: Permissions::empty(),
833 app_permissions: None,
834 thread_metadata: None,
835 },
836 )]),
837 members: HashMap::new(),
838 messages: HashMap::new(),
839 roles: HashMap::new(),
840 users: HashMap::new(),
841 }),
842 };
843
844 let text_input_tokens = [
845 Token::Struct {
846 name: "ModalInteractionComponent",
847 len: 4,
848 },
849 Token::String("type"),
850 Token::U8(ComponentType::TextInput.into()),
851 Token::String("custom_id"),
852 Token::String("the-text-input-id"),
853 Token::String("id"),
854 Token::I32(2),
855 Token::String("value"),
856 Token::String("input value"),
857 Token::StructEnd,
858 ];
859
860 let text_display_tokens = [
861 Token::Struct {
862 name: "ModalInteractionComponent",
863 len: 2,
864 },
865 Token::String("type"),
866 Token::U8(ComponentType::TextDisplay.into()),
867 Token::String("id"),
868 Token::I32(4),
869 Token::StructEnd,
870 ];
871
872 let channel_select_tokens = [
873 Token::Struct {
874 name: "ModalInteractionComponent",
875 len: 4,
876 },
877 Token::String("type"),
878 Token::U8(ComponentType::ChannelSelectMenu.into()),
879 Token::String("custom_id"),
880 Token::String("the-channel-select-id"),
881 Token::String("id"),
882 Token::I32(6),
883 Token::String("values"),
884 Token::Seq { len: Some(1) },
885 Token::NewtypeStruct { name: "Id" },
886 Token::String("42"),
887 Token::SeqEnd,
888 Token::StructEnd,
889 ];
890
891 let mut all_tokens = vec![
892 Token::Struct {
893 name: "ModalInteractionData",
894 len: 3,
895 },
896 Token::String("components"),
897 Token::Seq { len: Some(3) },
898 ];
899
900 all_tokens.extend_from_slice(&label_tokens(1, &text_input_tokens));
901 all_tokens.extend_from_slice(&label_tokens(3, &text_display_tokens));
902 all_tokens.extend_from_slice(&label_tokens(5, &channel_select_tokens));
903
904 all_tokens.extend_from_slice(&[
905 Token::SeqEnd,
906 Token::String("custom_id"),
907 Token::String("test-modal"),
908 Token::String("resolved"),
909 Token::Some,
910 Token::Struct {
911 name: "InteractionDataResolved",
912 len: 1,
913 },
914 Token::String("channels"),
915 Token::Map { len: Some(1) },
916 Token::NewtypeStruct { name: "Id" },
917 Token::String("42"),
918 Token::Struct {
919 name: "InteractionChannel",
920 len: 4,
921 },
922 Token::String("id"),
923 Token::NewtypeStruct { name: "Id" },
924 Token::String("42"),
925 Token::String("type"),
926 Token::U8(0),
927 Token::String("name"),
928 Token::String("the-channel-name"),
929 Token::String("permissions"),
930 Token::String("0"),
931 Token::StructEnd,
932 Token::MapEnd,
933 Token::StructEnd,
934 Token::StructEnd,
935 ]);
936
937 serde_test::assert_tokens(&value, &all_tokens);
938 }
939
940 #[test]
941 fn modal_file_upload() {
942 let value = ModalInteractionData {
943 custom_id: "test-modal".to_owned(),
944 components: vec![ModalInteractionComponent::FileUpload(
945 ModalInteractionFileUpload {
946 id: 42,
947 custom_id: "file-upload".to_owned(),
948 values: vec![Id::new(1), Id::new(2)],
949 },
950 )],
951 resolved: None,
954 };
955
956 serde_test::assert_tokens(
957 &value,
958 &[
959 Token::Struct {
960 name: "ModalInteractionData",
961 len: 2,
962 },
963 Token::String("components"),
964 Token::Seq { len: Some(1) },
965 Token::Struct {
966 name: "ModalInteractionComponent",
967 len: 4,
968 },
969 Token::String("type"),
970 Token::U8(ComponentType::FileUpload.into()),
971 Token::String("custom_id"),
972 Token::String("file-upload"),
973 Token::String("id"),
974 Token::I32(42),
975 Token::String("values"),
976 Token::Seq { len: Some(2) },
977 Token::NewtypeStruct { name: "Id" },
978 Token::Str("1"),
979 Token::NewtypeStruct { name: "Id" },
980 Token::Str("2"),
981 Token::SeqEnd,
982 Token::StructEnd,
983 Token::SeqEnd,
984 Token::String("custom_id"),
985 Token::String("test-modal"),
986 Token::StructEnd,
987 ],
988 )
989 }
990
991 #[test]
992 fn modal_checkbox() {
993 let value = ModalInteractionData {
994 custom_id: "test-modal".to_owned(),
995 components: vec![ModalInteractionComponent::Checkbox(
996 ModalInteractionCheckbox {
997 id: 10,
998 custom_id: "checkbox".to_owned(),
999 value: true,
1000 },
1001 )],
1002 resolved: None,
1003 };
1004
1005 serde_test::assert_tokens(
1006 &value,
1007 &[
1008 Token::Struct {
1009 name: "ModalInteractionData",
1010 len: 2,
1011 },
1012 Token::String("components"),
1013 Token::Seq { len: Some(1) },
1014 Token::Struct {
1015 name: "ModalInteractionComponent",
1016 len: 4,
1017 },
1018 Token::String("type"),
1019 Token::U8(ComponentType::Checkbox.into()),
1020 Token::String("custom_id"),
1021 Token::String("checkbox"),
1022 Token::String("id"),
1023 Token::I32(10),
1024 Token::String("value"),
1025 Token::Bool(true),
1026 Token::StructEnd,
1027 Token::SeqEnd,
1028 Token::String("custom_id"),
1029 Token::String("test-modal"),
1030 Token::StructEnd,
1031 ],
1032 )
1033 }
1034}