1use crate::{
2 application::command::CommandOptionType,
3 id::{
4 Id,
5 marker::{AttachmentMarker, ChannelMarker, GenericMarker, RoleMarker, UserMarker},
6 },
7};
8use serde::{
9 Deserialize, Deserializer, Serialize, Serializer,
10 de::{Error as DeError, IgnoredAny, MapAccess, Unexpected, Visitor},
11 ser::SerializeStruct,
12};
13use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
14
15#[derive(Clone, Debug, PartialEq)]
21pub struct CommandDataOption {
22 pub name: String,
24 pub value: CommandOptionValue,
26}
27
28impl Serialize for CommandDataOption {
29 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
30 let subcommand_is_empty = matches!(
31 &self.value,
32 CommandOptionValue::SubCommand(o)
33 | CommandOptionValue::SubCommandGroup(o)
34 if o.is_empty()
35 );
36
37 let focused = matches!(&self.value, CommandOptionValue::Focused(_, _));
38
39 let len = 2 + usize::from(!subcommand_is_empty) + usize::from(focused);
40
41 let mut state = serializer.serialize_struct("CommandDataOption", len)?;
42
43 if focused {
44 state.serialize_field("focused", &focused)?;
45 }
46
47 state.serialize_field("name", &self.name)?;
48
49 state.serialize_field("type", &self.value.kind())?;
50
51 match &self.value {
52 CommandOptionValue::Attachment(a) => state.serialize_field("value", a)?,
53 CommandOptionValue::Boolean(b) => state.serialize_field("value", b)?,
54 CommandOptionValue::Channel(c) => state.serialize_field("value", c)?,
55 CommandOptionValue::Focused(f, _) => state.serialize_field("value", f)?,
56 CommandOptionValue::Integer(i) => state.serialize_field("value", i)?,
57 CommandOptionValue::Mentionable(m) => state.serialize_field("value", m)?,
58 CommandOptionValue::Number(n) => state.serialize_field("value", n)?,
59 CommandOptionValue::Role(r) => state.serialize_field("value", r)?,
60 CommandOptionValue::String(s) => state.serialize_field("value", s)?,
61 CommandOptionValue::User(u) => state.serialize_field("value", u)?,
62 CommandOptionValue::SubCommand(s) | CommandOptionValue::SubCommandGroup(s) => {
63 if !subcommand_is_empty {
64 state.serialize_field("options", s)?
65 }
66 }
67 }
68
69 state.end()
70 }
71}
72
73impl<'de> Deserialize<'de> for CommandDataOption {
74 #[allow(clippy::too_many_lines)]
75 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
76 #[derive(Debug, Deserialize)]
77 #[serde(field_identifier, rename_all = "snake_case")]
78 enum Fields {
79 Focused,
80 Name,
81 Options,
82 Type,
83 Value,
84 }
85
86 #[derive(Debug, Deserialize)]
89 #[serde(untagged)]
90 enum ValueEnvelope {
91 Boolean(bool),
92 Integer(i64),
93 Number(f64),
94 String(String),
95 }
96
97 impl ValueEnvelope {
98 #[allow(clippy::missing_const_for_fn)]
99 fn as_unexpected(&self) -> Unexpected<'_> {
100 match self {
101 Self::Boolean(b) => Unexpected::Bool(*b),
102 Self::Integer(i) => Unexpected::Signed(*i),
103 Self::Number(f) => Unexpected::Float(*f),
104 Self::String(s) => Unexpected::Str(s),
105 }
106 }
107 }
108
109 impl Display for ValueEnvelope {
110 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
111 match self {
112 Self::Boolean(b) => Display::fmt(b, f),
113 Self::Integer(i) => Display::fmt(i, f),
114 Self::Number(n) => Display::fmt(n, f),
115 Self::String(s) => Display::fmt(s, f),
116 }
117 }
118 }
119
120 struct CommandDataOptionVisitor;
121
122 impl<'de> Visitor<'de> for CommandDataOptionVisitor {
123 type Value = CommandDataOption;
124
125 fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
126 formatter.write_str("CommandDataOption")
127 }
128
129 #[allow(clippy::too_many_lines)]
130 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
131 let mut name_opt = None;
132 let mut kind_opt = None;
133 let mut options = Vec::new();
134 let mut value_opt: Option<ValueEnvelope> = None;
135 let mut focused = None;
136
137 loop {
138 let key = match map.next_key() {
139 Ok(Some(key)) => key,
140 Ok(None) => break,
141 Err(_) => {
142 map.next_value::<IgnoredAny>()?;
143
144 continue;
145 }
146 };
147
148 match key {
149 Fields::Focused => {
150 if focused.is_some() {
151 return Err(DeError::duplicate_field("focused"));
152 }
153
154 focused = map.next_value()?;
155 }
156 Fields::Name => {
157 if name_opt.is_some() {
158 return Err(DeError::duplicate_field("name"));
159 }
160
161 name_opt = Some(map.next_value()?);
162 }
163 Fields::Options => {
164 if !options.is_empty() {
165 return Err(DeError::duplicate_field("options"));
166 }
167
168 options = map.next_value()?;
169 }
170 Fields::Type => {
171 if kind_opt.is_some() {
172 return Err(DeError::duplicate_field("type"));
173 }
174
175 kind_opt = Some(map.next_value()?);
176 }
177 Fields::Value => {
178 if value_opt.is_some() {
179 return Err(DeError::duplicate_field("value"));
180 }
181
182 value_opt = Some(map.next_value()?);
183 }
184 }
185 }
186
187 let focused = focused.unwrap_or_default();
188 let name = name_opt.ok_or_else(|| DeError::missing_field("name"))?;
189 let kind = kind_opt.ok_or_else(|| DeError::missing_field("type"))?;
190
191 let value = if focused {
192 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
193
194 CommandOptionValue::Focused(val.to_string(), kind)
195 } else {
196 match kind {
197 CommandOptionType::Attachment => {
198 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
199
200 if let ValueEnvelope::String(id) = &val {
201 CommandOptionValue::Attachment(id.parse().map_err(|_| {
202 DeError::invalid_type(val.as_unexpected(), &"attachment id")
203 })?)
204 } else {
205 return Err(DeError::invalid_type(
206 val.as_unexpected(),
207 &"attachment id",
208 ));
209 }
210 }
211 CommandOptionType::Boolean => {
212 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
213
214 if let ValueEnvelope::Boolean(b) = val {
215 CommandOptionValue::Boolean(b)
216 } else {
217 return Err(DeError::invalid_type(val.as_unexpected(), &"boolean"));
218 }
219 }
220 CommandOptionType::Channel => {
221 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
222
223 if let ValueEnvelope::String(id) = &val {
224 CommandOptionValue::Channel(id.parse().map_err(|_| {
225 DeError::invalid_type(val.as_unexpected(), &"channel id")
226 })?)
227 } else {
228 return Err(DeError::invalid_type(
229 val.as_unexpected(),
230 &"channel id",
231 ));
232 }
233 }
234 CommandOptionType::Integer => {
235 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
236
237 if let ValueEnvelope::Integer(i) = val {
238 CommandOptionValue::Integer(i)
239 } else {
240 return Err(DeError::invalid_type(val.as_unexpected(), &"integer"));
241 }
242 }
243 CommandOptionType::Mentionable => {
244 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
245
246 if let ValueEnvelope::String(id) = &val {
247 CommandOptionValue::Mentionable(id.parse().map_err(|_| {
248 DeError::invalid_type(val.as_unexpected(), &"mentionable id")
249 })?)
250 } else {
251 return Err(DeError::invalid_type(
252 val.as_unexpected(),
253 &"mentionable id",
254 ));
255 }
256 }
257 CommandOptionType::Number => {
258 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
259
260 match val {
261 ValueEnvelope::Integer(i) => {
262 #[allow(clippy::cast_precision_loss)]
268 CommandOptionValue::Number(i as f64)
269 }
270 ValueEnvelope::Number(f) => CommandOptionValue::Number(f),
271 other => {
272 return Err(DeError::invalid_type(
273 other.as_unexpected(),
274 &"number",
275 ));
276 }
277 }
278 }
279 CommandOptionType::Role => {
280 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
281
282 if let ValueEnvelope::String(id) = &val {
283 CommandOptionValue::Role(id.parse().map_err(|_| {
284 DeError::invalid_type(val.as_unexpected(), &"role id")
285 })?)
286 } else {
287 return Err(DeError::invalid_type(val.as_unexpected(), &"role id"));
288 }
289 }
290 CommandOptionType::String => {
291 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
292
293 if let ValueEnvelope::String(s) = val {
294 CommandOptionValue::String(s)
295 } else {
296 return Err(DeError::invalid_type(val.as_unexpected(), &"string"));
297 }
298 }
299 CommandOptionType::SubCommand => CommandOptionValue::SubCommand(options),
300 CommandOptionType::SubCommandGroup => {
301 CommandOptionValue::SubCommandGroup(options)
302 }
303 CommandOptionType::User => {
304 let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?;
305
306 if let ValueEnvelope::String(id) = &val {
307 CommandOptionValue::User(id.parse().map_err(|_| {
308 DeError::invalid_type(val.as_unexpected(), &"user id")
309 })?)
310 } else {
311 return Err(DeError::invalid_type(val.as_unexpected(), &"user id"));
312 }
313 }
314 }
315 };
316
317 Ok(CommandDataOption { name, value })
318 }
319 }
320
321 deserializer.deserialize_map(CommandDataOptionVisitor)
322 }
323}
324
325#[derive(Clone, Debug, PartialEq)]
327pub enum CommandOptionValue {
328 Attachment(Id<AttachmentMarker>),
330 Boolean(bool),
332 Channel(Id<ChannelMarker>),
334 Focused(String, CommandOptionType),
346 Integer(i64),
348 Mentionable(Id<GenericMarker>),
350 Number(f64),
352 Role(Id<RoleMarker>),
354 String(String),
356 SubCommand(Vec<CommandDataOption>),
358 SubCommandGroup(Vec<CommandDataOption>),
360 User(Id<UserMarker>),
362}
363
364impl CommandOptionValue {
365 pub const fn kind(&self) -> CommandOptionType {
366 match self {
367 CommandOptionValue::Attachment(_) => CommandOptionType::Attachment,
368 CommandOptionValue::Boolean(_) => CommandOptionType::Boolean,
369 CommandOptionValue::Channel(_) => CommandOptionType::Channel,
370 CommandOptionValue::Focused(_, t) => *t,
371 CommandOptionValue::Integer(_) => CommandOptionType::Integer,
372 CommandOptionValue::Mentionable(_) => CommandOptionType::Mentionable,
373 CommandOptionValue::Number(_) => CommandOptionType::Number,
374 CommandOptionValue::Role(_) => CommandOptionType::Role,
375 CommandOptionValue::String(_) => CommandOptionType::String,
376 CommandOptionValue::SubCommand(_) => CommandOptionType::SubCommand,
377 CommandOptionValue::SubCommandGroup(_) => CommandOptionType::SubCommandGroup,
378 CommandOptionValue::User(_) => CommandOptionType::User,
379 }
380 }
381}
382
383impl From<Id<AttachmentMarker>> for CommandOptionValue {
384 fn from(value: Id<AttachmentMarker>) -> Self {
385 CommandOptionValue::Attachment(value)
386 }
387}
388
389impl From<bool> for CommandOptionValue {
390 fn from(value: bool) -> Self {
391 CommandOptionValue::Boolean(value)
392 }
393}
394
395impl From<Id<ChannelMarker>> for CommandOptionValue {
396 fn from(value: Id<ChannelMarker>) -> Self {
397 CommandOptionValue::Channel(value)
398 }
399}
400
401impl From<(String, CommandOptionType)> for CommandOptionValue {
402 fn from((value, kind): (String, CommandOptionType)) -> Self {
403 CommandOptionValue::Focused(value, kind)
404 }
405}
406
407impl From<i64> for CommandOptionValue {
408 fn from(value: i64) -> Self {
409 CommandOptionValue::Integer(value)
410 }
411}
412
413impl From<Id<GenericMarker>> for CommandOptionValue {
414 fn from(value: Id<GenericMarker>) -> Self {
415 CommandOptionValue::Mentionable(value)
416 }
417}
418
419impl From<f64> for CommandOptionValue {
420 fn from(value: f64) -> Self {
421 CommandOptionValue::Number(value)
422 }
423}
424
425impl From<Id<RoleMarker>> for CommandOptionValue {
426 fn from(value: Id<RoleMarker>) -> Self {
427 CommandOptionValue::Role(value)
428 }
429}
430
431impl From<String> for CommandOptionValue {
432 fn from(value: String) -> Self {
433 CommandOptionValue::String(value)
434 }
435}
436
437impl From<Id<UserMarker>> for CommandOptionValue {
438 fn from(value: Id<UserMarker>) -> Self {
439 CommandOptionValue::User(value)
440 }
441}
442
443impl TryFrom<CommandOptionValue> for Id<AttachmentMarker> {
444 type Error = CommandOptionValue;
445
446 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
447 match value {
448 CommandOptionValue::Attachment(inner) => Ok(inner),
449 _ => Err(value),
450 }
451 }
452}
453
454impl TryFrom<CommandOptionValue> for bool {
455 type Error = CommandOptionValue;
456
457 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
458 match value {
459 CommandOptionValue::Boolean(inner) => Ok(inner),
460 _ => Err(value),
461 }
462 }
463}
464
465impl TryFrom<CommandOptionValue> for Id<ChannelMarker> {
466 type Error = CommandOptionValue;
467
468 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
469 match value {
470 CommandOptionValue::Channel(inner) => Ok(inner),
471 _ => Err(value),
472 }
473 }
474}
475
476impl TryFrom<CommandOptionValue> for (String, CommandOptionType) {
477 type Error = CommandOptionValue;
478
479 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
480 match value {
481 CommandOptionValue::Focused(value, kind) => Ok((value, kind)),
482 _ => Err(value),
483 }
484 }
485}
486
487impl TryFrom<CommandOptionValue> for i64 {
488 type Error = CommandOptionValue;
489
490 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
491 match value {
492 CommandOptionValue::Integer(inner) => Ok(inner),
493 _ => Err(value),
494 }
495 }
496}
497
498impl TryFrom<CommandOptionValue> for Id<GenericMarker> {
499 type Error = CommandOptionValue;
500
501 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
502 match value {
503 CommandOptionValue::Mentionable(inner) => Ok(inner),
504 _ => Err(value),
505 }
506 }
507}
508
509impl TryFrom<CommandOptionValue> for f64 {
510 type Error = CommandOptionValue;
511
512 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
513 match value {
514 CommandOptionValue::Number(inner) => Ok(inner),
515 _ => Err(value),
516 }
517 }
518}
519
520impl TryFrom<CommandOptionValue> for Id<RoleMarker> {
521 type Error = CommandOptionValue;
522
523 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
524 match value {
525 CommandOptionValue::Role(inner) => Ok(inner),
526 _ => Err(value),
527 }
528 }
529}
530
531impl TryFrom<CommandOptionValue> for String {
532 type Error = CommandOptionValue;
533
534 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
535 match value {
536 CommandOptionValue::String(inner) => Ok(inner),
537 _ => Err(value),
538 }
539 }
540}
541
542impl TryFrom<CommandOptionValue> for Id<UserMarker> {
543 type Error = CommandOptionValue;
544
545 fn try_from(value: CommandOptionValue) -> Result<Self, Self::Error> {
546 match value {
547 CommandOptionValue::User(inner) => Ok(inner),
548 _ => Err(value),
549 }
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use crate::{
556 application::{
557 command::{CommandOptionType, CommandType},
558 interaction::application_command::{
559 CommandData, CommandDataOption, CommandOptionValue,
560 },
561 },
562 id::Id,
563 };
564 use serde_test::Token;
565
566 #[test]
567 fn no_options() {
568 let value = CommandData {
569 guild_id: Some(Id::new(2)),
570 id: Id::new(1),
571 kind: CommandType::ChatInput,
572 name: "permissions".to_owned(),
573 options: Vec::new(),
574 resolved: None,
575 target_id: None,
576 };
577 serde_test::assert_tokens(
578 &value,
579 &[
580 Token::Struct {
581 name: "CommandData",
582 len: 4,
583 },
584 Token::Str("guild_id"),
585 Token::Some,
586 Token::NewtypeStruct { name: "Id" },
587 Token::Str("2"),
588 Token::Str("id"),
589 Token::NewtypeStruct { name: "Id" },
590 Token::Str("1"),
591 Token::Str("type"),
592 Token::U8(CommandType::ChatInput.into()),
593 Token::Str("name"),
594 Token::Str("permissions"),
595 Token::StructEnd,
596 ],
597 )
598 }
599
600 #[test]
601 fn with_option() {
602 let value = CommandData {
603 guild_id: Some(Id::new(2)),
604 id: Id::new(1),
605 kind: CommandType::ChatInput,
606 name: "permissions".to_owned(),
607 options: Vec::from([CommandDataOption {
608 name: "cat".to_owned(),
609 value: CommandOptionValue::Integer(42),
610 }]),
611 resolved: None,
612 target_id: None,
613 };
614
615 serde_test::assert_tokens(
616 &value,
617 &[
618 Token::Struct {
619 name: "CommandData",
620 len: 5,
621 },
622 Token::Str("guild_id"),
623 Token::Some,
624 Token::NewtypeStruct { name: "Id" },
625 Token::Str("2"),
626 Token::Str("id"),
627 Token::NewtypeStruct { name: "Id" },
628 Token::Str("1"),
629 Token::Str("type"),
630 Token::U8(CommandType::ChatInput.into()),
631 Token::Str("name"),
632 Token::Str("permissions"),
633 Token::Str("options"),
634 Token::Seq { len: Some(1) },
635 Token::Struct {
636 name: "CommandDataOption",
637 len: 3,
638 },
639 Token::Str("name"),
640 Token::Str("cat"),
641 Token::Str("type"),
642 Token::U8(CommandOptionType::Integer as u8),
643 Token::Str("value"),
644 Token::I64(42),
645 Token::StructEnd,
646 Token::SeqEnd,
647 Token::StructEnd,
648 ],
649 )
650 }
651
652 #[test]
653 fn with_normal_option_and_autocomplete() {
654 let value = CommandData {
655 guild_id: Some(Id::new(2)),
656 id: Id::new(1),
657 kind: CommandType::ChatInput,
658 name: "permissions".to_owned(),
659 options: Vec::from([
660 CommandDataOption {
661 name: "cat".to_owned(),
662 value: CommandOptionValue::Integer(42),
663 },
664 CommandDataOption {
665 name: "dog".to_owned(),
666 value: CommandOptionValue::Focused(
667 "Shiba".to_owned(),
668 CommandOptionType::String,
669 ),
670 },
671 ]),
672 resolved: None,
673 target_id: None,
674 };
675
676 serde_test::assert_de_tokens(
677 &value,
678 &[
679 Token::Struct {
680 name: "CommandData",
681 len: 5,
682 },
683 Token::Str("guild_id"),
684 Token::Some,
685 Token::NewtypeStruct { name: "Id" },
686 Token::Str("2"),
687 Token::Str("id"),
688 Token::NewtypeStruct { name: "Id" },
689 Token::Str("1"),
690 Token::Str("type"),
691 Token::U8(CommandType::ChatInput.into()),
692 Token::Str("name"),
693 Token::Str("permissions"),
694 Token::Str("options"),
695 Token::Seq { len: Some(2) },
696 Token::Struct {
697 name: "CommandDataOption",
698 len: 3,
699 },
700 Token::Str("name"),
701 Token::Str("cat"),
702 Token::Str("type"),
703 Token::U8(CommandOptionType::Integer as u8),
704 Token::Str("value"),
705 Token::I64(42),
706 Token::StructEnd,
707 Token::Struct {
708 name: "CommandDataOption",
709 len: 4,
710 },
711 Token::Str("focused"),
712 Token::Some,
713 Token::Bool(true),
714 Token::Str("name"),
715 Token::Str("dog"),
716 Token::Str("type"),
717 Token::U8(CommandOptionType::String as u8),
718 Token::Str("value"),
719 Token::String("Shiba"),
720 Token::StructEnd,
721 Token::SeqEnd,
722 Token::StructEnd,
723 ],
724 )
725 }
726
727 #[test]
728 fn subcommand_without_option() {
729 let value = CommandData {
730 guild_id: None,
731 id: Id::new(1),
732 kind: CommandType::ChatInput,
733 name: "photo".to_owned(),
734 options: Vec::from([CommandDataOption {
735 name: "cat".to_owned(),
736 value: CommandOptionValue::SubCommand(Vec::new()),
737 }]),
738 resolved: None,
739 target_id: None,
740 };
741
742 serde_test::assert_tokens(
743 &value,
744 &[
745 Token::Struct {
746 name: "CommandData",
747 len: 4,
748 },
749 Token::Str("id"),
750 Token::NewtypeStruct { name: "Id" },
751 Token::Str("1"),
752 Token::Str("type"),
753 Token::U8(CommandType::ChatInput.into()),
754 Token::Str("name"),
755 Token::Str("photo"),
756 Token::Str("options"),
757 Token::Seq { len: Some(1) },
758 Token::Struct {
759 name: "CommandDataOption",
760 len: 2,
761 },
762 Token::Str("name"),
763 Token::Str("cat"),
764 Token::Str("type"),
765 Token::U8(CommandOptionType::SubCommand as u8),
766 Token::StructEnd,
767 Token::SeqEnd,
768 Token::StructEnd,
769 ],
770 );
771 }
772
773 #[test]
774 fn numbers() {
775 let value = CommandDataOption {
776 name: "opt".to_string(),
777 value: CommandOptionValue::Number(5.0),
778 };
779
780 serde_test::assert_de_tokens(
781 &value,
782 &[
783 Token::Struct {
784 name: "CommandDataOption",
785 len: 3,
786 },
787 Token::Str("name"),
788 Token::Str("opt"),
789 Token::Str("type"),
790 Token::U8(CommandOptionType::Number as u8),
791 Token::Str("value"),
792 Token::I64(5),
793 Token::StructEnd,
794 ],
795 );
796 }
797
798 #[test]
799 fn autocomplete() {
800 let value = CommandDataOption {
801 name: "opt".to_string(),
802 value: CommandOptionValue::Focused(
803 "not a number".to_owned(),
804 CommandOptionType::Number,
805 ),
806 };
807
808 serde_test::assert_de_tokens(
809 &value,
810 &[
811 Token::Struct {
812 name: "CommandDataOption",
813 len: 4,
814 },
815 Token::Str("focused"),
816 Token::Some,
817 Token::Bool(true),
818 Token::Str("name"),
819 Token::Str("opt"),
820 Token::Str("type"),
821 Token::U8(CommandOptionType::Number as u8),
822 Token::Str("value"),
823 Token::String("not a number"),
824 Token::StructEnd,
825 ],
826 );
827 }
828
829 #[test]
830 fn autocomplete_number() {
831 let value = CommandDataOption {
832 name: "opt".to_string(),
833 value: CommandOptionValue::Focused("1".to_owned(), CommandOptionType::Number),
834 };
835
836 serde_test::assert_de_tokens(
837 &value,
838 &[
839 Token::Struct {
840 name: "CommandDataOption",
841 len: 4,
842 },
843 Token::Str("focused"),
844 Token::Some,
845 Token::Bool(true),
846 Token::Str("name"),
847 Token::Str("opt"),
848 Token::Str("type"),
849 Token::U8(CommandOptionType::Number as u8),
850 Token::Str("value"),
851 Token::String("1"),
852 Token::StructEnd,
853 ],
854 );
855 }
856
857 #[test]
858 fn leading_zeroes_string_option_value() {
859 let value = CommandDataOption {
860 name: "opt".to_string(),
861 value: CommandOptionValue::String("0001".to_owned()),
862 };
863
864 serde_test::assert_de_tokens(
865 &value,
866 &[
867 Token::Struct {
868 name: "CommandDataOption",
869 len: 3,
870 },
871 Token::Str("name"),
872 Token::Str("opt"),
873 Token::Str("type"),
874 Token::U8(CommandOptionType::String as u8),
875 Token::Str("value"),
876 Token::String("0001"),
877 Token::StructEnd,
878 ],
879 );
880 }
881}