1use twilight_model::{
48 application::{
49 command::{
50 Command, CommandOption, CommandOptionChoice, CommandOptionChoiceValue,
51 CommandOptionType, CommandType,
52 },
53 interaction::InteractionContextType,
54 },
55 channel::ChannelType,
56 guild::Permissions,
57 id::{Id, marker::GuildMarker},
58 oauth::ApplicationIntegrationType,
59};
60use twilight_validate::command::{CommandValidationError, command as validate_command};
61
62#[derive(Clone, Debug)]
64#[must_use = "must be built into a command"]
65pub struct CommandBuilder(Command);
66
67impl CommandBuilder {
68 #[must_use = "builders have no effect if unused"]
70 #[allow(deprecated)]
71 pub fn new(name: impl Into<String>, description: impl Into<String>, kind: CommandType) -> Self {
72 Self(Command {
73 application_id: None,
74 default_member_permissions: None,
75 dm_permission: None,
76 description: description.into(),
77 description_localizations: None,
78 guild_id: None,
79 id: None,
80 kind,
81 name: name.into(),
82 name_localizations: None,
83 nsfw: None,
84 options: Vec::new(),
85 version: Id::new(1),
86 contexts: None,
87 integration_types: None,
88 })
89 }
90
91 #[must_use = "must be built into a command"]
93 pub fn build(self) -> Command {
94 self.0
95 }
96
97 pub fn validate(self) -> Result<Self, CommandValidationError> {
104 validate_command(&self.0)?;
105
106 Ok(self)
107 }
108
109 pub const fn guild_id(mut self, guild_id: Id<GuildMarker>) -> Self {
113 self.0.guild_id = Some(guild_id);
114
115 self
116 }
117
118 pub fn contexts(mut self, contexts: impl IntoIterator<Item = InteractionContextType>) -> Self {
122 self.0.contexts = Some(contexts.into_iter().collect());
123
124 self
125 }
126
127 pub const fn default_member_permissions(
131 mut self,
132 default_member_permissions: Permissions,
133 ) -> Self {
134 self.0.default_member_permissions = Some(default_member_permissions);
135
136 self
137 }
138
139 #[deprecated(note = "use contexts instead")]
143 #[allow(deprecated)]
144 pub const fn dm_permission(mut self, dm_permission: bool) -> Self {
145 self.0.dm_permission = Some(dm_permission);
146
147 self
148 }
149
150 pub fn description_localizations<K: Into<String>, V: Into<String>>(
154 mut self,
155 localizations: impl IntoIterator<Item = (K, V)>,
156 ) -> Self {
157 self.0.description_localizations = Some(
158 localizations
159 .into_iter()
160 .map(|(a, b)| (a.into(), b.into()))
161 .collect(),
162 );
163
164 self
165 }
166
167 pub fn integration_types(
171 mut self,
172 integration_types: impl IntoIterator<Item = ApplicationIntegrationType>,
173 ) -> Self {
174 self.0.integration_types = Some(integration_types.into_iter().collect());
175
176 self
177 }
178
179 pub fn name_localizations<K: Into<String>, V: Into<String>>(
183 mut self,
184 localizations: impl IntoIterator<Item = (K, V)>,
185 ) -> Self {
186 self.0.name_localizations = Some(
187 localizations
188 .into_iter()
189 .map(|(a, b)| (a.into(), b.into()))
190 .collect(),
191 );
192
193 self
194 }
195
196 pub fn option(self, option: impl Into<CommandOption>) -> Self {
200 self._option(option.into())
201 }
202
203 fn _option(mut self, option: CommandOption) -> Self {
204 self.0.options.push(option);
205
206 self
207 }
208
209 pub const fn nsfw(mut self, nsfw: bool) -> Self {
213 self.0.nsfw = Some(nsfw);
214
215 self
216 }
217}
218
219#[derive(Clone, Debug)]
221#[must_use = "should be used in a command builder"]
222pub struct AttachmentBuilder(CommandOption);
223
224impl AttachmentBuilder {
225 #[must_use = "builders have no effect if unused"]
227 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
228 Self(CommandOption {
229 autocomplete: None,
230 channel_types: None,
231 choices: None,
232 description: description.into(),
233 description_localizations: None,
234 kind: CommandOptionType::Attachment,
235 max_length: None,
236 max_value: None,
237 min_length: None,
238 min_value: None,
239 name: name.into(),
240 name_localizations: None,
241 options: None,
242 required: None,
243 })
244 }
245
246 #[must_use = "should be used in a command builder"]
248 pub fn build(self) -> CommandOption {
249 self.0
250 }
251
252 pub fn description_localizations<K: Into<String>, V: Into<String>>(
256 mut self,
257 localizations: impl IntoIterator<Item = (K, V)>,
258 ) -> Self {
259 self.0.description_localizations = Some(
260 localizations
261 .into_iter()
262 .map(|(a, b)| (a.into(), b.into()))
263 .collect(),
264 );
265
266 self
267 }
268
269 pub fn name_localizations<K: Into<String>, V: Into<String>>(
273 mut self,
274 localizations: impl IntoIterator<Item = (K, V)>,
275 ) -> Self {
276 self.0.name_localizations = Some(
277 localizations
278 .into_iter()
279 .map(|(a, b)| (a.into(), b.into()))
280 .collect(),
281 );
282
283 self
284 }
285
286 pub const fn required(mut self, required: bool) -> Self {
290 self.0.required = Some(required);
291
292 self
293 }
294}
295
296impl From<AttachmentBuilder> for CommandOption {
297 fn from(builder: AttachmentBuilder) -> CommandOption {
298 builder.build()
299 }
300}
301
302#[derive(Clone, Debug)]
304#[must_use = "should be used in a command builder"]
305pub struct BooleanBuilder(CommandOption);
306
307impl BooleanBuilder {
308 #[must_use = "builders have no effect if unused"]
310 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
311 Self(CommandOption {
312 autocomplete: None,
313 channel_types: None,
314 choices: None,
315 description: description.into(),
316 description_localizations: None,
317 kind: CommandOptionType::Boolean,
318 max_length: None,
319 max_value: None,
320 min_length: None,
321 min_value: None,
322 name: name.into(),
323 name_localizations: None,
324 options: None,
325 required: None,
326 })
327 }
328
329 #[must_use = "should be used in a command builder"]
331 pub fn build(self) -> CommandOption {
332 self.0
333 }
334
335 pub fn description_localizations<K: Into<String>, V: Into<String>>(
339 mut self,
340 localizations: impl IntoIterator<Item = (K, V)>,
341 ) -> Self {
342 self.0.description_localizations = Some(
343 localizations
344 .into_iter()
345 .map(|(a, b)| (a.into(), b.into()))
346 .collect(),
347 );
348
349 self
350 }
351
352 pub fn name_localizations<K: Into<String>, V: Into<String>>(
356 mut self,
357 localizations: impl IntoIterator<Item = (K, V)>,
358 ) -> Self {
359 self.0.name_localizations = Some(
360 localizations
361 .into_iter()
362 .map(|(a, b)| (a.into(), b.into()))
363 .collect(),
364 );
365
366 self
367 }
368
369 pub const fn required(mut self, required: bool) -> Self {
373 self.0.required = Some(required);
374
375 self
376 }
377}
378
379impl From<BooleanBuilder> for CommandOption {
380 fn from(builder: BooleanBuilder) -> CommandOption {
381 builder.build()
382 }
383}
384
385#[derive(Clone, Debug)]
387#[must_use = "should be used in a command builder"]
388pub struct ChannelBuilder(CommandOption);
389
390impl ChannelBuilder {
391 #[must_use = "builders have no effect if unused"]
393 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
394 Self(CommandOption {
395 autocomplete: None,
396 channel_types: Some(Vec::new()),
397 choices: None,
398 description: description.into(),
399 description_localizations: None,
400 kind: CommandOptionType::Channel,
401 max_length: None,
402 max_value: None,
403 min_length: None,
404 min_value: None,
405 name: name.into(),
406 name_localizations: None,
407 options: None,
408 required: None,
409 })
410 }
411
412 #[must_use = "should be used in a command builder"]
414 pub fn build(self) -> CommandOption {
415 self.0
416 }
417
418 pub fn channel_types(mut self, channel_types: impl IntoIterator<Item = ChannelType>) -> Self {
422 self.0.channel_types = Some(Vec::from_iter(channel_types));
423
424 self
425 }
426
427 pub fn description_localizations<K: Into<String>, V: Into<String>>(
431 mut self,
432 localizations: impl IntoIterator<Item = (K, V)>,
433 ) -> Self {
434 self.0.description_localizations = Some(
435 localizations
436 .into_iter()
437 .map(|(a, b)| (a.into(), b.into()))
438 .collect(),
439 );
440
441 self
442 }
443
444 pub fn name_localizations<K: Into<String>, V: Into<String>>(
448 mut self,
449 localizations: impl IntoIterator<Item = (K, V)>,
450 ) -> Self {
451 self.0.name_localizations = Some(
452 localizations
453 .into_iter()
454 .map(|(a, b)| (a.into(), b.into()))
455 .collect(),
456 );
457
458 self
459 }
460
461 pub const fn required(mut self, required: bool) -> Self {
465 self.0.required = Some(required);
466
467 self
468 }
469}
470
471impl From<ChannelBuilder> for CommandOption {
472 fn from(builder: ChannelBuilder) -> CommandOption {
473 builder.build()
474 }
475}
476#[derive(Clone, Debug)]
478#[must_use = "should be used in a command builder"]
479pub struct IntegerBuilder(CommandOption);
480
481impl IntegerBuilder {
482 #[must_use = "builders have no effect if unused"]
484 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
485 Self(CommandOption {
486 autocomplete: Some(false),
487 channel_types: None,
488 choices: Some(Vec::new()),
489 description: description.into(),
490 description_localizations: None,
491 kind: CommandOptionType::Integer,
492 max_length: None,
493 max_value: None,
494 min_length: None,
495 min_value: None,
496 name: name.into(),
497 name_localizations: None,
498 options: None,
499 required: None,
500 })
501 }
502
503 #[must_use = "should be used in a command builder"]
505 pub fn build(self) -> CommandOption {
506 self.0
507 }
508
509 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
513 self.0.autocomplete = Some(autocomplete);
514
515 self
516 }
517
518 #[track_caller]
529 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
530 mut self,
531 choice_name: &str,
532 name_localizations: impl IntoIterator<Item = (K, V)>,
533 ) -> Self {
534 let choice = self
535 .0
536 .choices
537 .as_mut()
538 .expect("choice exists")
539 .iter_mut()
540 .find(|choice| choice.name == choice_name)
541 .expect("choice exists");
542
543 choice.name_localizations = Some(
544 name_localizations
545 .into_iter()
546 .map(|(k, v)| (k.into(), v.into()))
547 .collect(),
548 );
549
550 self
551 }
552
553 pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, i64)>) -> Self {
562 self.0.choices = Some(
563 choices
564 .into_iter()
565 .map(|(name, value, ..)| CommandOptionChoice {
566 name: name.into(),
567 name_localizations: None,
568 value: value.into(),
569 })
570 .collect(),
571 );
572
573 self
574 }
575
576 pub fn description_localizations<K: Into<String>, V: Into<String>>(
580 mut self,
581 localizations: impl IntoIterator<Item = (K, V)>,
582 ) -> Self {
583 self.0.description_localizations = Some(
584 localizations
585 .into_iter()
586 .map(|(a, b)| (a.into(), b.into()))
587 .collect(),
588 );
589
590 self
591 }
592
593 pub fn max_value(mut self, value: i64) -> Self {
597 self.0.max_value = Some(value.into());
598
599 self
600 }
601
602 pub fn min_value(mut self, value: i64) -> Self {
606 self.0.min_value = Some(value.into());
607
608 self
609 }
610
611 pub fn name_localizations<K: Into<String>, V: Into<String>>(
615 mut self,
616 localizations: impl IntoIterator<Item = (K, V)>,
617 ) -> Self {
618 self.0.name_localizations = Some(
619 localizations
620 .into_iter()
621 .map(|(a, b)| (a.into(), b.into()))
622 .collect(),
623 );
624
625 self
626 }
627
628 pub const fn required(mut self, required: bool) -> Self {
632 self.0.required = Some(required);
633
634 self
635 }
636}
637
638impl From<IntegerBuilder> for CommandOption {
639 fn from(builder: IntegerBuilder) -> CommandOption {
640 builder.build()
641 }
642}
643
644#[derive(Clone, Debug)]
646#[must_use = "should be used in a command builder"]
647pub struct MentionableBuilder(CommandOption);
648
649impl MentionableBuilder {
650 #[must_use = "builders have no effect if unused"]
652 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
653 Self(CommandOption {
654 autocomplete: None,
655 channel_types: None,
656 choices: None,
657 description: description.into(),
658 description_localizations: None,
659 kind: CommandOptionType::Mentionable,
660 max_length: None,
661 max_value: None,
662 min_length: None,
663 min_value: None,
664 name: name.into(),
665 name_localizations: None,
666 options: None,
667 required: None,
668 })
669 }
670
671 #[must_use = "should be used in a command builder"]
673 pub fn build(self) -> CommandOption {
674 self.0
675 }
676
677 pub fn description_localizations<K: Into<String>, V: Into<String>>(
681 mut self,
682 localizations: impl IntoIterator<Item = (K, V)>,
683 ) -> Self {
684 self.0.description_localizations = Some(
685 localizations
686 .into_iter()
687 .map(|(a, b)| (a.into(), b.into()))
688 .collect(),
689 );
690
691 self
692 }
693
694 pub fn name_localizations<K: Into<String>, V: Into<String>>(
698 mut self,
699 localizations: impl IntoIterator<Item = (K, V)>,
700 ) -> Self {
701 self.0.name_localizations = Some(
702 localizations
703 .into_iter()
704 .map(|(a, b)| (a.into(), b.into()))
705 .collect(),
706 );
707
708 self
709 }
710
711 pub const fn required(mut self, required: bool) -> Self {
715 self.0.required = Some(required);
716
717 self
718 }
719}
720
721impl From<MentionableBuilder> for CommandOption {
722 fn from(builder: MentionableBuilder) -> CommandOption {
723 builder.build()
724 }
725}
726
727#[derive(Clone, Debug)]
729#[must_use = "should be used in a command builder"]
730pub struct NumberBuilder(CommandOption);
731
732impl NumberBuilder {
733 #[must_use = "builders have no effect if unused"]
735 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
736 Self(CommandOption {
737 autocomplete: Some(false),
738 channel_types: None,
739 choices: Some(Vec::new()),
740 description: description.into(),
741 description_localizations: None,
742 kind: CommandOptionType::Number,
743 max_length: None,
744 max_value: None,
745 min_length: None,
746 min_value: None,
747 name: name.into(),
748 name_localizations: None,
749 options: None,
750 required: None,
751 })
752 }
753
754 #[must_use = "should be used in a command builder"]
756 pub fn build(self) -> CommandOption {
757 self.0
758 }
759
760 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
764 self.0.autocomplete = Some(autocomplete);
765
766 self
767 }
768
769 #[track_caller]
780 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
781 mut self,
782 choice_name: &str,
783 name_localizations: impl IntoIterator<Item = (K, V)>,
784 ) -> Self {
785 let choice = self
786 .0
787 .choices
788 .as_mut()
789 .expect("choice exists")
790 .iter_mut()
791 .find(|choice| choice.name == choice_name)
792 .expect("choice exists");
793
794 choice.name_localizations = Some(
795 name_localizations
796 .into_iter()
797 .map(|(k, v)| (k.into(), v.into()))
798 .collect(),
799 );
800
801 self
802 }
803
804 pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, f64)>) -> Self {
813 self.0.choices = Some(
814 choices
815 .into_iter()
816 .map(|(name, value, ..)| CommandOptionChoice {
817 name: name.into(),
818 name_localizations: None,
819 value: value.into(),
820 })
821 .collect(),
822 );
823
824 self
825 }
826
827 pub fn description_localizations<K: Into<String>, V: Into<String>>(
831 mut self,
832 localizations: impl IntoIterator<Item = (K, V)>,
833 ) -> Self {
834 self.0.description_localizations = Some(
835 localizations
836 .into_iter()
837 .map(|(a, b)| (a.into(), b.into()))
838 .collect(),
839 );
840
841 self
842 }
843
844 pub fn max_value(mut self, value: f64) -> Self {
848 self.0.max_value = Some(value.into());
849
850 self
851 }
852
853 pub fn min_value(mut self, value: f64) -> Self {
857 self.0.min_value = Some(value.into());
858
859 self
860 }
861
862 pub fn name_localizations<K: Into<String>, V: Into<String>>(
866 mut self,
867 localizations: impl IntoIterator<Item = (K, V)>,
868 ) -> Self {
869 self.0.name_localizations = Some(
870 localizations
871 .into_iter()
872 .map(|(a, b)| (a.into(), b.into()))
873 .collect(),
874 );
875
876 self
877 }
878
879 pub const fn required(mut self, required: bool) -> Self {
883 self.0.required = Some(required);
884
885 self
886 }
887}
888
889impl From<NumberBuilder> for CommandOption {
890 fn from(builder: NumberBuilder) -> CommandOption {
891 builder.build()
892 }
893}
894
895#[derive(Clone, Debug)]
897#[must_use = "should be used in a command builder"]
898pub struct RoleBuilder(CommandOption);
899
900impl RoleBuilder {
901 #[must_use = "builders have no effect if unused"]
903 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
904 Self(CommandOption {
905 autocomplete: None,
906 channel_types: None,
907 choices: None,
908 description: description.into(),
909 description_localizations: None,
910 kind: CommandOptionType::Role,
911 max_length: None,
912 max_value: None,
913 min_length: None,
914 min_value: None,
915 name: name.into(),
916 name_localizations: None,
917 options: None,
918 required: None,
919 })
920 }
921
922 #[must_use = "should be used in a command builder"]
924 pub fn build(self) -> CommandOption {
925 self.0
926 }
927
928 pub fn description_localizations<K: Into<String>, V: Into<String>>(
932 mut self,
933 localizations: impl IntoIterator<Item = (K, V)>,
934 ) -> Self {
935 self.0.description_localizations = Some(
936 localizations
937 .into_iter()
938 .map(|(a, b)| (a.into(), b.into()))
939 .collect(),
940 );
941
942 self
943 }
944
945 pub fn name_localizations<K: Into<String>, V: Into<String>>(
949 mut self,
950 localizations: impl IntoIterator<Item = (K, V)>,
951 ) -> Self {
952 self.0.name_localizations = Some(
953 localizations
954 .into_iter()
955 .map(|(a, b)| (a.into(), b.into()))
956 .collect(),
957 );
958
959 self
960 }
961
962 pub const fn required(mut self, required: bool) -> Self {
966 self.0.required = Some(required);
967
968 self
969 }
970}
971
972impl From<RoleBuilder> for CommandOption {
973 fn from(builder: RoleBuilder) -> CommandOption {
974 builder.build()
975 }
976}
977
978#[derive(Clone, Debug)]
980#[must_use = "should be used in a command builder"]
981pub struct StringBuilder(CommandOption);
982
983impl StringBuilder {
984 #[must_use = "builders have no effect if unused"]
986 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
987 Self(CommandOption {
988 autocomplete: Some(false),
989 channel_types: None,
990 choices: Some(Vec::new()),
991 description: description.into(),
992 description_localizations: None,
993 kind: CommandOptionType::String,
994 max_length: None,
995 max_value: None,
996 min_length: None,
997 min_value: None,
998 name: name.into(),
999 name_localizations: None,
1000 options: None,
1001 required: None,
1002 })
1003 }
1004
1005 #[must_use = "should be used in a command builder"]
1007 pub fn build(self) -> CommandOption {
1008 self.0
1009 }
1010
1011 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
1015 self.0.autocomplete = Some(autocomplete);
1016
1017 self
1018 }
1019
1020 #[track_caller]
1031 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
1032 mut self,
1033 choice_name: &str,
1034 name_localizations: impl IntoIterator<Item = (K, V)>,
1035 ) -> Self {
1036 let choice = self
1037 .0
1038 .choices
1039 .as_mut()
1040 .expect("choice exists")
1041 .iter_mut()
1042 .find(|choice| choice.name == choice_name)
1043 .expect("choice exists");
1044
1045 choice.name_localizations = Some(
1046 name_localizations
1047 .into_iter()
1048 .map(|(k, v)| (k.into(), v.into()))
1049 .collect(),
1050 );
1051
1052 self
1053 }
1054
1055 pub fn choices<K: Into<String>, V: Into<String>>(
1064 mut self,
1065 choices: impl IntoIterator<Item = (K, V)>,
1066 ) -> Self {
1067 self.0.choices = Some(
1068 choices
1069 .into_iter()
1070 .map(|(name, value, ..)| CommandOptionChoice {
1071 name: name.into(),
1072 name_localizations: None,
1073 value: CommandOptionChoiceValue::from(value.into()),
1074 })
1075 .collect(),
1076 );
1077
1078 self
1079 }
1080
1081 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1085 mut self,
1086 localizations: impl IntoIterator<Item = (K, V)>,
1087 ) -> Self {
1088 self.0.description_localizations = Some(
1089 localizations
1090 .into_iter()
1091 .map(|(a, b)| (a.into(), b.into()))
1092 .collect(),
1093 );
1094
1095 self
1096 }
1097
1098 pub const fn max_length(mut self, value: u16) -> Self {
1102 self.0.max_length = Some(value);
1103
1104 self
1105 }
1106
1107 pub const fn min_length(mut self, value: u16) -> Self {
1111 self.0.min_length = Some(value);
1112
1113 self
1114 }
1115
1116 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1120 mut self,
1121 localizations: impl IntoIterator<Item = (K, V)>,
1122 ) -> Self {
1123 self.0.name_localizations = Some(
1124 localizations
1125 .into_iter()
1126 .map(|(a, b)| (a.into(), b.into()))
1127 .collect(),
1128 );
1129
1130 self
1131 }
1132
1133 pub const fn required(mut self, required: bool) -> Self {
1137 self.0.required = Some(required);
1138
1139 self
1140 }
1141}
1142
1143impl From<StringBuilder> for CommandOption {
1144 fn from(builder: StringBuilder) -> CommandOption {
1145 builder.build()
1146 }
1147}
1148
1149#[derive(Clone, Debug)]
1151#[must_use = "should be used in a command builder"]
1152pub struct SubCommandBuilder(CommandOption);
1153
1154impl SubCommandBuilder {
1155 #[must_use = "builders have no effect if unused"]
1157 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1158 Self(CommandOption {
1159 autocomplete: None,
1160 channel_types: None,
1161 choices: None,
1162 description: description.into(),
1163 description_localizations: None,
1164 kind: CommandOptionType::SubCommand,
1165 max_length: None,
1166 max_value: None,
1167 min_length: None,
1168 min_value: None,
1169 name: name.into(),
1170 name_localizations: None,
1171 options: Some(Vec::new()),
1172 required: None,
1173 })
1174 }
1175
1176 #[must_use = "should be used in a command builder"]
1178 pub fn build(self) -> CommandOption {
1179 self.0
1180 }
1181
1182 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1186 mut self,
1187 localizations: impl IntoIterator<Item = (K, V)>,
1188 ) -> Self {
1189 self.0.description_localizations = Some(
1190 localizations
1191 .into_iter()
1192 .map(|(a, b)| (a.into(), b.into()))
1193 .collect(),
1194 );
1195
1196 self
1197 }
1198
1199 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1203 mut self,
1204 localizations: impl IntoIterator<Item = (K, V)>,
1205 ) -> Self {
1206 self.0.name_localizations = Some(
1207 localizations
1208 .into_iter()
1209 .map(|(a, b)| (a.into(), b.into()))
1210 .collect(),
1211 );
1212
1213 self
1214 }
1215
1216 pub fn option(self, option: impl Into<CommandOption>) -> Self {
1220 self._option(option.into())
1221 }
1222
1223 fn _option(mut self, option: CommandOption) -> Self {
1224 self.0
1225 .options
1226 .as_mut()
1227 .expect("set to Some in `new`")
1228 .push(option);
1229
1230 self
1231 }
1232}
1233
1234impl From<SubCommandBuilder> for CommandOption {
1235 fn from(builder: SubCommandBuilder) -> CommandOption {
1236 builder.build()
1237 }
1238}
1239
1240#[derive(Clone, Debug)]
1242#[must_use = "should be used in a command builder"]
1243pub struct SubCommandGroupBuilder(CommandOption);
1244
1245impl SubCommandGroupBuilder {
1246 #[must_use = "builders have no effect if unused"]
1248 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1249 Self(CommandOption {
1250 autocomplete: None,
1251 channel_types: None,
1252 choices: None,
1253 description: description.into(),
1254 description_localizations: None,
1255 kind: CommandOptionType::SubCommandGroup,
1256 max_length: None,
1257 max_value: None,
1258 min_length: None,
1259 min_value: None,
1260 name: name.into(),
1261 name_localizations: None,
1262 options: Some(Vec::new()),
1263 required: None,
1264 })
1265 }
1266
1267 #[must_use = "should be used in a command builder"]
1269 pub fn build(self) -> CommandOption {
1270 self.0
1271 }
1272
1273 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1277 mut self,
1278 localizations: impl IntoIterator<Item = (K, V)>,
1279 ) -> Self {
1280 self.0.description_localizations = Some(
1281 localizations
1282 .into_iter()
1283 .map(|(a, b)| (a.into(), b.into()))
1284 .collect(),
1285 );
1286
1287 self
1288 }
1289
1290 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1294 mut self,
1295 localizations: impl IntoIterator<Item = (K, V)>,
1296 ) -> Self {
1297 self.0.name_localizations = Some(
1298 localizations
1299 .into_iter()
1300 .map(|(a, b)| (a.into(), b.into()))
1301 .collect(),
1302 );
1303
1304 self
1305 }
1306
1307 pub fn subcommands(mut self, subcommands: impl IntoIterator<Item = SubCommandBuilder>) -> Self {
1311 self.0.options = Some(subcommands.into_iter().map(Into::into).collect());
1312
1313 self
1314 }
1315}
1316
1317impl From<SubCommandGroupBuilder> for CommandOption {
1318 fn from(builder: SubCommandGroupBuilder) -> CommandOption {
1319 builder.build()
1320 }
1321}
1322
1323#[derive(Clone, Debug)]
1325#[must_use = "should be used in a command builder"]
1326pub struct UserBuilder(CommandOption);
1327
1328impl UserBuilder {
1329 #[must_use = "builders have no effect if unused"]
1331 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1332 Self(CommandOption {
1333 autocomplete: None,
1334 channel_types: None,
1335 choices: None,
1336 description: description.into(),
1337 description_localizations: None,
1338 kind: CommandOptionType::User,
1339 max_length: None,
1340 max_value: None,
1341 min_length: None,
1342 min_value: None,
1343 name: name.into(),
1344 name_localizations: None,
1345 options: None,
1346 required: None,
1347 })
1348 }
1349
1350 #[must_use = "should be used in a command builder"]
1352 pub fn build(self) -> CommandOption {
1353 self.0
1354 }
1355
1356 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1360 mut self,
1361 localizations: impl IntoIterator<Item = (K, V)>,
1362 ) -> Self {
1363 self.0.description_localizations = Some(
1364 localizations
1365 .into_iter()
1366 .map(|(a, b)| (a.into(), b.into()))
1367 .collect(),
1368 );
1369
1370 self
1371 }
1372
1373 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1377 mut self,
1378 localizations: impl IntoIterator<Item = (K, V)>,
1379 ) -> Self {
1380 self.0.name_localizations = Some(
1381 localizations
1382 .into_iter()
1383 .map(|(a, b)| (a.into(), b.into()))
1384 .collect(),
1385 );
1386
1387 self
1388 }
1389
1390 pub const fn required(mut self, required: bool) -> Self {
1394 self.0.required = Some(required);
1395
1396 self
1397 }
1398}
1399
1400impl From<UserBuilder> for CommandOption {
1401 fn from(builder: UserBuilder) -> CommandOption {
1402 builder.build()
1403 }
1404}
1405
1406#[cfg(test)]
1407mod tests {
1408 use super::*;
1409 use static_assertions::assert_impl_all;
1410 use std::fmt::Debug;
1411
1412 assert_impl_all!(AttachmentBuilder: Clone, Debug, Send, Sync);
1413 assert_impl_all!(CommandBuilder: Clone, Debug, Send, Sync);
1414 assert_impl_all!(BooleanBuilder: Clone, Debug, Send, Sync);
1415 assert_impl_all!(ChannelBuilder: Clone, Debug, Send, Sync);
1416 assert_impl_all!(IntegerBuilder: Clone, Debug, Send, Sync);
1417 assert_impl_all!(MentionableBuilder: Clone, Debug, Send, Sync);
1418 assert_impl_all!(RoleBuilder: Clone, Debug, Send, Sync);
1419 assert_impl_all!(StringBuilder: Clone, Debug, Send, Sync);
1420 assert_impl_all!(SubCommandBuilder: Clone, Debug, Send, Sync);
1421 assert_impl_all!(SubCommandGroupBuilder: Clone, Debug, Send, Sync);
1422 assert_impl_all!(UserBuilder: Clone, Debug, Send, Sync);
1423
1424 #[test]
1425 #[allow(clippy::too_many_lines, deprecated)]
1426 fn construct_command_with_builder() {
1427 let command =
1428 CommandBuilder::new(
1429 "permissions",
1430 "Get or edit permissions for a user or a role",
1431 CommandType::ChatInput,
1432 )
1433 .nsfw(true)
1434 .option(
1435 SubCommandGroupBuilder::new("user", "Get or edit permissions for a user")
1436 .subcommands([
1437 SubCommandBuilder::new("get", "Get permissions for a user")
1438 .option(UserBuilder::new("user", "The user to get").required(true))
1439 .option(ChannelBuilder::new(
1440 "channel",
1441 "The channel permissions to get. If omitted, the guild \
1442 permissions will be returned",
1443 )),
1444 SubCommandBuilder::new("edit", "Edit permissions for a user")
1445 .option(UserBuilder::new("user", "The user to edit").required(true))
1446 .option(ChannelBuilder::new(
1447 "channel",
1448 "The channel permissions to edit. If omitted, the guild \
1449 permissions will be edited",
1450 )),
1451 ]),
1452 )
1453 .option(
1454 SubCommandGroupBuilder::new("role", "Get or edit permissions for a role")
1455 .subcommands([
1456 SubCommandBuilder::new("get", "Get permissions for a role")
1457 .option(RoleBuilder::new("role", "The role to get").required(true))
1458 .option(ChannelBuilder::new(
1459 "channel",
1460 "The channel permissions to get. If omitted, the guild \
1461 permissions will be returned",
1462 )),
1463 SubCommandBuilder::new("edit", "Edit permissions for a role")
1464 .option(RoleBuilder::new("role", "The role to edit").required(true))
1465 .option(ChannelBuilder::new(
1466 "channel",
1467 "The channel permissions to edit. If omitted, the guild \
1468 permissions will be edited",
1469 )),
1470 ]),
1471 )
1472 .build();
1473
1474 let command_manual = Command {
1475 application_id: None,
1476 contexts: None,
1477 default_member_permissions: None,
1478 dm_permission: None,
1479 description: String::from("Get or edit permissions for a user or a role"),
1480 description_localizations: None,
1481 guild_id: None,
1482 id: None,
1483 integration_types: None,
1484 kind: CommandType::ChatInput,
1485 name: String::from("permissions"),
1486 name_localizations: None,
1487 nsfw: Some(true),
1488 options: Vec::from([
1489 CommandOption {
1490 autocomplete: None,
1491 channel_types: None,
1492 choices: None,
1493 description: "Get or edit permissions for a user".to_owned(),
1494 description_localizations: None,
1495 kind: CommandOptionType::SubCommandGroup,
1496 max_length: None,
1497 max_value: None,
1498 min_length: None,
1499 min_value: None,
1500 name: "user".to_owned(),
1501 name_localizations: None,
1502 options: Some(Vec::from([
1503 CommandOption {
1504 autocomplete: None,
1505 channel_types: None,
1506 choices: None,
1507 description: "Get permissions for a user".to_owned(),
1508 description_localizations: None,
1509 kind: CommandOptionType::SubCommand,
1510 max_length: None,
1511 max_value: None,
1512 min_length: None,
1513 min_value: None,
1514 name: "get".to_owned(),
1515 name_localizations: None,
1516 options: Some(Vec::from([
1517 CommandOption {
1518 autocomplete: None,
1519 channel_types: None,
1520 choices: None,
1521 description: "The user to get".to_owned(),
1522 description_localizations: None,
1523 kind: CommandOptionType::User,
1524 max_length: None,
1525 max_value: None,
1526 min_length: None,
1527 min_value: None,
1528 name: "user".to_owned(),
1529 name_localizations: None,
1530 options: None,
1531 required: Some(true),
1532 },
1533 CommandOption {
1534 autocomplete: None,
1535 channel_types: Some(Vec::new()),
1536 choices: None,
1537 description:
1538 "The channel permissions to get. If omitted, the guild \
1539 permissions will be returned"
1540 .to_owned(),
1541 description_localizations: None,
1542 kind: CommandOptionType::Channel,
1543 max_length: None,
1544 max_value: None,
1545 min_length: None,
1546 min_value: None,
1547 name: "channel".to_owned(),
1548 name_localizations: None,
1549 options: None,
1550 required: None,
1551 },
1552 ])),
1553 required: None,
1554 },
1555 CommandOption {
1556 autocomplete: None,
1557 channel_types: None,
1558 choices: None,
1559 description: "Edit permissions for a user".to_owned(),
1560 description_localizations: None,
1561 kind: CommandOptionType::SubCommand,
1562 max_length: None,
1563 max_value: None,
1564 min_length: None,
1565 min_value: None,
1566 name: "edit".to_owned(),
1567 name_localizations: None,
1568 options: Some(Vec::from([
1569 CommandOption {
1570 autocomplete: None,
1571 channel_types: None,
1572 choices: None,
1573 description: "The user to edit".to_owned(),
1574 description_localizations: None,
1575 kind: CommandOptionType::User,
1576 max_length: None,
1577 max_value: None,
1578 min_length: None,
1579 min_value: None,
1580 name: "user".to_owned(),
1581 name_localizations: None,
1582 options: None,
1583 required: Some(true),
1584 },
1585 CommandOption {
1586 autocomplete: None,
1587 channel_types: Some(Vec::new()),
1588 choices: None,
1589 description:
1590 "The channel permissions to edit. If omitted, the guild \
1591 permissions will be edited"
1592 .to_owned(),
1593 description_localizations: None,
1594 kind: CommandOptionType::Channel,
1595 max_length: None,
1596 max_value: None,
1597 min_length: None,
1598 min_value: None,
1599 name: "channel".to_owned(),
1600 name_localizations: None,
1601 options: None,
1602 required: None,
1603 },
1604 ])),
1605 required: None,
1606 },
1607 ])),
1608 required: None,
1609 },
1610 CommandOption {
1611 autocomplete: None,
1612 channel_types: None,
1613 choices: None,
1614 description: "Get or edit permissions for a role".to_owned(),
1615 description_localizations: None,
1616 kind: CommandOptionType::SubCommandGroup,
1617 max_length: None,
1618 max_value: None,
1619 min_length: None,
1620 min_value: None,
1621 name: "role".to_owned(),
1622 name_localizations: None,
1623 options: Some(Vec::from([
1624 CommandOption {
1625 autocomplete: None,
1626 channel_types: None,
1627 choices: None,
1628 description: "Get permissions for a role".to_owned(),
1629 description_localizations: None,
1630 kind: CommandOptionType::SubCommand,
1631 max_length: None,
1632 max_value: None,
1633 min_length: None,
1634 min_value: None,
1635 name: "get".to_owned(),
1636 name_localizations: None,
1637 options: Some(Vec::from([
1638 CommandOption {
1639 autocomplete: None,
1640 channel_types: None,
1641 choices: None,
1642 description: "The role to get".to_owned(),
1643 description_localizations: None,
1644 kind: CommandOptionType::Role,
1645 max_length: None,
1646 max_value: None,
1647 min_length: None,
1648 min_value: None,
1649 name: "role".to_owned(),
1650 name_localizations: None,
1651 options: None,
1652 required: Some(true),
1653 },
1654 CommandOption {
1655 autocomplete: None,
1656 channel_types: Some(Vec::new()),
1657 choices: None,
1658 description:
1659 "The channel permissions to get. If omitted, the guild \
1660 permissions will be returned"
1661 .to_owned(),
1662 description_localizations: None,
1663 kind: CommandOptionType::Channel,
1664 max_length: None,
1665 max_value: None,
1666 min_length: None,
1667 min_value: None,
1668 name: "channel".to_owned(),
1669 name_localizations: None,
1670 options: None,
1671 required: None,
1672 },
1673 ])),
1674 required: None,
1675 },
1676 CommandOption {
1677 autocomplete: None,
1678 channel_types: None,
1679 choices: None,
1680 description: "Edit permissions for a role".to_owned(),
1681 description_localizations: None,
1682 kind: CommandOptionType::SubCommand,
1683 max_length: None,
1684 max_value: None,
1685 min_length: None,
1686 min_value: None,
1687 name: "edit".to_owned(),
1688 name_localizations: None,
1689 options: Some(Vec::from([
1690 CommandOption {
1691 autocomplete: None,
1692 channel_types: None,
1693 choices: None,
1694 description: "The role to edit".to_owned(),
1695 description_localizations: None,
1696 kind: CommandOptionType::Role,
1697 max_length: None,
1698 max_value: None,
1699 min_length: None,
1700 min_value: None,
1701 name: "role".to_owned(),
1702 name_localizations: None,
1703 options: None,
1704 required: Some(true),
1705 },
1706 CommandOption {
1707 autocomplete: None,
1708 channel_types: Some(Vec::new()),
1709 choices: None,
1710 description:
1711 "The channel permissions to edit. If omitted, the guild \
1712 permissions will be edited"
1713 .to_owned(),
1714 description_localizations: None,
1715 kind: CommandOptionType::Channel,
1716 max_length: None,
1717 max_value: None,
1718 min_length: None,
1719 min_value: None,
1720 name: "channel".to_owned(),
1721 name_localizations: None,
1722 options: None,
1723 required: None,
1724 },
1725 ])),
1726 required: None,
1727 },
1728 ])),
1729 required: None,
1730 },
1731 ]),
1732 version: Id::new(1),
1733 };
1734
1735 assert_eq!(command, command_manual);
1736 }
1737
1738 #[test]
1739 fn validate() {
1740 let result = CommandBuilder::new("", "", CommandType::ChatInput).validate();
1741
1742 assert!(result.is_err());
1743 }
1744}