1use twilight_model::{
48 application::{
49 command::{
50 Command, CommandOption, CommandOptionChoice, CommandOptionChoiceValue,
51 CommandOptionType, CommandOptionValue, CommandType,
52 },
53 interaction::InteractionContextType,
54 },
55 channel::ChannelType,
56 guild::Permissions,
57 id::{marker::GuildMarker, Id},
58 oauth::ApplicationIntegrationType,
59};
60use twilight_validate::command::{command as validate_command, CommandValidationError};
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 #[allow(clippy::missing_const_for_fn)]
93 #[must_use = "must be built into a command"]
94 pub fn build(self) -> Command {
95 self.0
96 }
97
98 pub fn validate(self) -> Result<Self, CommandValidationError> {
105 validate_command(&self.0)?;
106
107 Ok(self)
108 }
109
110 pub const fn guild_id(mut self, guild_id: Id<GuildMarker>) -> Self {
114 self.0.guild_id = Some(guild_id);
115
116 self
117 }
118
119 pub fn contexts(mut self, contexts: impl IntoIterator<Item = InteractionContextType>) -> Self {
123 self.0.contexts = Some(contexts.into_iter().collect());
124
125 self
126 }
127
128 pub const fn default_member_permissions(
132 mut self,
133 default_member_permissions: Permissions,
134 ) -> Self {
135 self.0.default_member_permissions = Some(default_member_permissions);
136
137 self
138 }
139
140 #[deprecated(note = "use contexts instead")]
144 #[allow(deprecated)]
145 pub const fn dm_permission(mut self, dm_permission: bool) -> Self {
146 self.0.dm_permission = Some(dm_permission);
147
148 self
149 }
150
151 pub fn description_localizations<K: Into<String>, V: Into<String>>(
155 mut self,
156 localizations: impl IntoIterator<Item = (K, V)>,
157 ) -> Self {
158 self.0.description_localizations = Some(
159 localizations
160 .into_iter()
161 .map(|(a, b)| (a.into(), b.into()))
162 .collect(),
163 );
164
165 self
166 }
167
168 pub fn integration_types(
172 mut self,
173 integration_types: impl IntoIterator<Item = ApplicationIntegrationType>,
174 ) -> Self {
175 self.0.integration_types = Some(integration_types.into_iter().collect());
176
177 self
178 }
179
180 pub fn name_localizations<K: Into<String>, V: Into<String>>(
184 mut self,
185 localizations: impl IntoIterator<Item = (K, V)>,
186 ) -> Self {
187 self.0.name_localizations = Some(
188 localizations
189 .into_iter()
190 .map(|(a, b)| (a.into(), b.into()))
191 .collect(),
192 );
193
194 self
195 }
196
197 pub fn option(self, option: impl Into<CommandOption>) -> Self {
201 self._option(option.into())
202 }
203
204 fn _option(mut self, option: CommandOption) -> Self {
205 self.0.options.push(option);
206
207 self
208 }
209
210 pub const fn nsfw(mut self, nsfw: bool) -> Self {
214 self.0.nsfw = Some(nsfw);
215
216 self
217 }
218}
219
220#[derive(Clone, Debug)]
222#[must_use = "should be used in a command builder"]
223pub struct AttachmentBuilder(CommandOption);
224
225impl AttachmentBuilder {
226 #[must_use = "builders have no effect if unused"]
228 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
229 Self(CommandOption {
230 autocomplete: None,
231 channel_types: None,
232 choices: None,
233 description: description.into(),
234 description_localizations: None,
235 kind: CommandOptionType::Attachment,
236 max_length: None,
237 max_value: None,
238 min_length: None,
239 min_value: None,
240 name: name.into(),
241 name_localizations: None,
242 options: None,
243 required: None,
244 })
245 }
246
247 #[allow(clippy::missing_const_for_fn)]
249 #[must_use = "should be used in a command builder"]
250 pub fn build(self) -> CommandOption {
251 self.0
252 }
253
254 pub fn description_localizations<K: Into<String>, V: Into<String>>(
258 mut self,
259 localizations: impl IntoIterator<Item = (K, V)>,
260 ) -> Self {
261 self.0.description_localizations = Some(
262 localizations
263 .into_iter()
264 .map(|(a, b)| (a.into(), b.into()))
265 .collect(),
266 );
267
268 self
269 }
270
271 pub fn name_localizations<K: Into<String>, V: Into<String>>(
275 mut self,
276 localizations: impl IntoIterator<Item = (K, V)>,
277 ) -> Self {
278 self.0.name_localizations = Some(
279 localizations
280 .into_iter()
281 .map(|(a, b)| (a.into(), b.into()))
282 .collect(),
283 );
284
285 self
286 }
287
288 pub const fn required(mut self, required: bool) -> Self {
292 self.0.required = Some(required);
293
294 self
295 }
296}
297
298impl From<AttachmentBuilder> for CommandOption {
299 fn from(builder: AttachmentBuilder) -> CommandOption {
300 builder.build()
301 }
302}
303
304#[derive(Clone, Debug)]
306#[must_use = "should be used in a command builder"]
307pub struct BooleanBuilder(CommandOption);
308
309impl BooleanBuilder {
310 #[must_use = "builders have no effect if unused"]
312 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
313 Self(CommandOption {
314 autocomplete: None,
315 channel_types: None,
316 choices: None,
317 description: description.into(),
318 description_localizations: None,
319 kind: CommandOptionType::Boolean,
320 max_length: None,
321 max_value: None,
322 min_length: None,
323 min_value: None,
324 name: name.into(),
325 name_localizations: None,
326 options: None,
327 required: None,
328 })
329 }
330
331 #[allow(clippy::missing_const_for_fn)]
333 #[must_use = "should be used in a command builder"]
334 pub fn build(self) -> CommandOption {
335 self.0
336 }
337
338 pub fn description_localizations<K: Into<String>, V: Into<String>>(
342 mut self,
343 localizations: impl IntoIterator<Item = (K, V)>,
344 ) -> Self {
345 self.0.description_localizations = Some(
346 localizations
347 .into_iter()
348 .map(|(a, b)| (a.into(), b.into()))
349 .collect(),
350 );
351
352 self
353 }
354
355 pub fn name_localizations<K: Into<String>, V: Into<String>>(
359 mut self,
360 localizations: impl IntoIterator<Item = (K, V)>,
361 ) -> Self {
362 self.0.name_localizations = Some(
363 localizations
364 .into_iter()
365 .map(|(a, b)| (a.into(), b.into()))
366 .collect(),
367 );
368
369 self
370 }
371
372 pub const fn required(mut self, required: bool) -> Self {
376 self.0.required = Some(required);
377
378 self
379 }
380}
381
382impl From<BooleanBuilder> for CommandOption {
383 fn from(builder: BooleanBuilder) -> CommandOption {
384 builder.build()
385 }
386}
387
388#[derive(Clone, Debug)]
390#[must_use = "should be used in a command builder"]
391pub struct ChannelBuilder(CommandOption);
392
393impl ChannelBuilder {
394 #[must_use = "builders have no effect if unused"]
396 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
397 Self(CommandOption {
398 autocomplete: None,
399 channel_types: Some(Vec::new()),
400 choices: None,
401 description: description.into(),
402 description_localizations: None,
403 kind: CommandOptionType::Channel,
404 max_length: None,
405 max_value: None,
406 min_length: None,
407 min_value: None,
408 name: name.into(),
409 name_localizations: None,
410 options: None,
411 required: None,
412 })
413 }
414
415 #[allow(clippy::missing_const_for_fn)]
417 #[must_use = "should be used in a command builder"]
418 pub fn build(self) -> CommandOption {
419 self.0
420 }
421
422 pub fn channel_types(mut self, channel_types: impl IntoIterator<Item = ChannelType>) -> Self {
426 self.0.channel_types = Some(Vec::from_iter(channel_types));
427
428 self
429 }
430
431 pub fn description_localizations<K: Into<String>, V: Into<String>>(
435 mut self,
436 localizations: impl IntoIterator<Item = (K, V)>,
437 ) -> Self {
438 self.0.description_localizations = Some(
439 localizations
440 .into_iter()
441 .map(|(a, b)| (a.into(), b.into()))
442 .collect(),
443 );
444
445 self
446 }
447
448 pub fn name_localizations<K: Into<String>, V: Into<String>>(
452 mut self,
453 localizations: impl IntoIterator<Item = (K, V)>,
454 ) -> Self {
455 self.0.name_localizations = Some(
456 localizations
457 .into_iter()
458 .map(|(a, b)| (a.into(), b.into()))
459 .collect(),
460 );
461
462 self
463 }
464
465 pub const fn required(mut self, required: bool) -> Self {
469 self.0.required = Some(required);
470
471 self
472 }
473}
474
475impl From<ChannelBuilder> for CommandOption {
476 fn from(builder: ChannelBuilder) -> CommandOption {
477 builder.build()
478 }
479}
480#[derive(Clone, Debug)]
482#[must_use = "should be used in a command builder"]
483pub struct IntegerBuilder(CommandOption);
484
485impl IntegerBuilder {
486 #[must_use = "builders have no effect if unused"]
488 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
489 Self(CommandOption {
490 autocomplete: Some(false),
491 channel_types: None,
492 choices: Some(Vec::new()),
493 description: description.into(),
494 description_localizations: None,
495 kind: CommandOptionType::Integer,
496 max_length: None,
497 max_value: None,
498 min_length: None,
499 min_value: None,
500 name: name.into(),
501 name_localizations: None,
502 options: None,
503 required: None,
504 })
505 }
506
507 #[allow(clippy::missing_const_for_fn)]
509 #[must_use = "should be used in a command builder"]
510 pub fn build(self) -> CommandOption {
511 self.0
512 }
513
514 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
518 self.0.autocomplete = Some(autocomplete);
519
520 self
521 }
522
523 #[track_caller]
534 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
535 mut self,
536 choice_name: &str,
537 name_localizations: impl IntoIterator<Item = (K, V)>,
538 ) -> Self {
539 let choice = self
540 .0
541 .choices
542 .as_mut()
543 .expect("choice exists")
544 .iter_mut()
545 .find(|choice| choice.name == choice_name)
546 .expect("choice exists");
547
548 choice.name_localizations = Some(
549 name_localizations
550 .into_iter()
551 .map(|(k, v)| (k.into(), v.into()))
552 .collect(),
553 );
554
555 self
556 }
557
558 pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, i64)>) -> Self {
567 self.0.choices = Some(
568 choices
569 .into_iter()
570 .map(|(name, value, ..)| CommandOptionChoice {
571 name: name.into(),
572 name_localizations: None,
573 value: CommandOptionChoiceValue::Integer(value),
574 })
575 .collect(),
576 );
577
578 self
579 }
580
581 pub fn description_localizations<K: Into<String>, V: Into<String>>(
585 mut self,
586 localizations: impl IntoIterator<Item = (K, V)>,
587 ) -> Self {
588 self.0.description_localizations = Some(
589 localizations
590 .into_iter()
591 .map(|(a, b)| (a.into(), b.into()))
592 .collect(),
593 );
594
595 self
596 }
597
598 pub const fn max_value(mut self, value: i64) -> Self {
602 self.0.max_value = Some(CommandOptionValue::Integer(value));
603
604 self
605 }
606
607 pub const fn min_value(mut self, value: i64) -> Self {
611 self.0.min_value = Some(CommandOptionValue::Integer(value));
612
613 self
614 }
615
616 pub fn name_localizations<K: Into<String>, V: Into<String>>(
620 mut self,
621 localizations: impl IntoIterator<Item = (K, V)>,
622 ) -> Self {
623 self.0.name_localizations = Some(
624 localizations
625 .into_iter()
626 .map(|(a, b)| (a.into(), b.into()))
627 .collect(),
628 );
629
630 self
631 }
632
633 pub const fn required(mut self, required: bool) -> Self {
637 self.0.required = Some(required);
638
639 self
640 }
641}
642
643impl From<IntegerBuilder> for CommandOption {
644 fn from(builder: IntegerBuilder) -> CommandOption {
645 builder.build()
646 }
647}
648
649#[derive(Clone, Debug)]
651#[must_use = "should be used in a command builder"]
652pub struct MentionableBuilder(CommandOption);
653
654impl MentionableBuilder {
655 #[must_use = "builders have no effect if unused"]
657 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
658 Self(CommandOption {
659 autocomplete: None,
660 channel_types: None,
661 choices: None,
662 description: description.into(),
663 description_localizations: None,
664 kind: CommandOptionType::Mentionable,
665 max_length: None,
666 max_value: None,
667 min_length: None,
668 min_value: None,
669 name: name.into(),
670 name_localizations: None,
671 options: None,
672 required: None,
673 })
674 }
675
676 #[allow(clippy::missing_const_for_fn)]
678 #[must_use = "should be used in a command builder"]
679 pub fn build(self) -> CommandOption {
680 self.0
681 }
682
683 pub fn description_localizations<K: Into<String>, V: Into<String>>(
687 mut self,
688 localizations: impl IntoIterator<Item = (K, V)>,
689 ) -> Self {
690 self.0.description_localizations = Some(
691 localizations
692 .into_iter()
693 .map(|(a, b)| (a.into(), b.into()))
694 .collect(),
695 );
696
697 self
698 }
699
700 pub fn name_localizations<K: Into<String>, V: Into<String>>(
704 mut self,
705 localizations: impl IntoIterator<Item = (K, V)>,
706 ) -> Self {
707 self.0.name_localizations = Some(
708 localizations
709 .into_iter()
710 .map(|(a, b)| (a.into(), b.into()))
711 .collect(),
712 );
713
714 self
715 }
716
717 pub const fn required(mut self, required: bool) -> Self {
721 self.0.required = Some(required);
722
723 self
724 }
725}
726
727impl From<MentionableBuilder> for CommandOption {
728 fn from(builder: MentionableBuilder) -> CommandOption {
729 builder.build()
730 }
731}
732
733#[derive(Clone, Debug)]
735#[must_use = "should be used in a command builder"]
736pub struct NumberBuilder(CommandOption);
737
738impl NumberBuilder {
739 #[must_use = "builders have no effect if unused"]
741 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
742 Self(CommandOption {
743 autocomplete: Some(false),
744 channel_types: None,
745 choices: Some(Vec::new()),
746 description: description.into(),
747 description_localizations: None,
748 kind: CommandOptionType::Number,
749 max_length: None,
750 max_value: None,
751 min_length: None,
752 min_value: None,
753 name: name.into(),
754 name_localizations: None,
755 options: None,
756 required: None,
757 })
758 }
759
760 #[allow(clippy::missing_const_for_fn)]
762 #[must_use = "should be used in a command builder"]
763 pub fn build(self) -> CommandOption {
764 self.0
765 }
766
767 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
771 self.0.autocomplete = Some(autocomplete);
772
773 self
774 }
775
776 #[track_caller]
787 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
788 mut self,
789 choice_name: &str,
790 name_localizations: impl IntoIterator<Item = (K, V)>,
791 ) -> Self {
792 let choice = self
793 .0
794 .choices
795 .as_mut()
796 .expect("choice exists")
797 .iter_mut()
798 .find(|choice| choice.name == choice_name)
799 .expect("choice exists");
800
801 choice.name_localizations = Some(
802 name_localizations
803 .into_iter()
804 .map(|(k, v)| (k.into(), v.into()))
805 .collect(),
806 );
807
808 self
809 }
810
811 pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, f64)>) -> Self {
820 self.0.choices = Some(
821 choices
822 .into_iter()
823 .map(|(name, value, ..)| CommandOptionChoice {
824 name: name.into(),
825 name_localizations: None,
826 value: CommandOptionChoiceValue::Number(value),
827 })
828 .collect(),
829 );
830
831 self
832 }
833
834 pub fn description_localizations<K: Into<String>, V: Into<String>>(
838 mut self,
839 localizations: impl IntoIterator<Item = (K, V)>,
840 ) -> Self {
841 self.0.description_localizations = Some(
842 localizations
843 .into_iter()
844 .map(|(a, b)| (a.into(), b.into()))
845 .collect(),
846 );
847
848 self
849 }
850
851 pub const fn max_value(mut self, value: f64) -> Self {
855 self.0.max_value = Some(CommandOptionValue::Number(value));
856
857 self
858 }
859
860 pub const fn min_value(mut self, value: f64) -> Self {
864 self.0.min_value = Some(CommandOptionValue::Number(value));
865
866 self
867 }
868
869 pub fn name_localizations<K: Into<String>, V: Into<String>>(
873 mut self,
874 localizations: impl IntoIterator<Item = (K, V)>,
875 ) -> Self {
876 self.0.name_localizations = Some(
877 localizations
878 .into_iter()
879 .map(|(a, b)| (a.into(), b.into()))
880 .collect(),
881 );
882
883 self
884 }
885
886 pub const fn required(mut self, required: bool) -> Self {
890 self.0.required = Some(required);
891
892 self
893 }
894}
895
896impl From<NumberBuilder> for CommandOption {
897 fn from(builder: NumberBuilder) -> CommandOption {
898 builder.build()
899 }
900}
901
902#[derive(Clone, Debug)]
904#[must_use = "should be used in a command builder"]
905pub struct RoleBuilder(CommandOption);
906
907impl RoleBuilder {
908 #[must_use = "builders have no effect if unused"]
910 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
911 Self(CommandOption {
912 autocomplete: None,
913 channel_types: None,
914 choices: None,
915 description: description.into(),
916 description_localizations: None,
917 kind: CommandOptionType::Role,
918 max_length: None,
919 max_value: None,
920 min_length: None,
921 min_value: None,
922 name: name.into(),
923 name_localizations: None,
924 options: None,
925 required: None,
926 })
927 }
928
929 #[allow(clippy::missing_const_for_fn)]
931 #[must_use = "should be used in a command builder"]
932 pub fn build(self) -> CommandOption {
933 self.0
934 }
935
936 pub fn description_localizations<K: Into<String>, V: Into<String>>(
940 mut self,
941 localizations: impl IntoIterator<Item = (K, V)>,
942 ) -> Self {
943 self.0.description_localizations = Some(
944 localizations
945 .into_iter()
946 .map(|(a, b)| (a.into(), b.into()))
947 .collect(),
948 );
949
950 self
951 }
952
953 pub fn name_localizations<K: Into<String>, V: Into<String>>(
957 mut self,
958 localizations: impl IntoIterator<Item = (K, V)>,
959 ) -> Self {
960 self.0.name_localizations = Some(
961 localizations
962 .into_iter()
963 .map(|(a, b)| (a.into(), b.into()))
964 .collect(),
965 );
966
967 self
968 }
969
970 pub const fn required(mut self, required: bool) -> Self {
974 self.0.required = Some(required);
975
976 self
977 }
978}
979
980impl From<RoleBuilder> for CommandOption {
981 fn from(builder: RoleBuilder) -> CommandOption {
982 builder.build()
983 }
984}
985
986#[derive(Clone, Debug)]
988#[must_use = "should be used in a command builder"]
989pub struct StringBuilder(CommandOption);
990
991impl StringBuilder {
992 #[must_use = "builders have no effect if unused"]
994 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
995 Self(CommandOption {
996 autocomplete: Some(false),
997 channel_types: None,
998 choices: Some(Vec::new()),
999 description: description.into(),
1000 description_localizations: None,
1001 kind: CommandOptionType::String,
1002 max_length: None,
1003 max_value: None,
1004 min_length: None,
1005 min_value: None,
1006 name: name.into(),
1007 name_localizations: None,
1008 options: None,
1009 required: None,
1010 })
1011 }
1012
1013 #[allow(clippy::missing_const_for_fn)]
1015 #[must_use = "should be used in a command builder"]
1016 pub fn build(self) -> CommandOption {
1017 self.0
1018 }
1019
1020 pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
1024 self.0.autocomplete = Some(autocomplete);
1025
1026 self
1027 }
1028
1029 #[track_caller]
1040 pub fn choice_localizations<K: Into<String>, V: Into<String>>(
1041 mut self,
1042 choice_name: &str,
1043 name_localizations: impl IntoIterator<Item = (K, V)>,
1044 ) -> Self {
1045 let choice = self
1046 .0
1047 .choices
1048 .as_mut()
1049 .expect("choice exists")
1050 .iter_mut()
1051 .find(|choice| choice.name == choice_name)
1052 .expect("choice exists");
1053
1054 choice.name_localizations = Some(
1055 name_localizations
1056 .into_iter()
1057 .map(|(k, v)| (k.into(), v.into()))
1058 .collect(),
1059 );
1060
1061 self
1062 }
1063
1064 pub fn choices<K: Into<String>, V: Into<String>>(
1073 mut self,
1074 choices: impl IntoIterator<Item = (K, V)>,
1075 ) -> Self {
1076 self.0.choices = Some(
1077 choices
1078 .into_iter()
1079 .map(|(name, value, ..)| CommandOptionChoice {
1080 name: name.into(),
1081 name_localizations: None,
1082 value: CommandOptionChoiceValue::String(value.into()),
1083 })
1084 .collect(),
1085 );
1086
1087 self
1088 }
1089
1090 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1094 mut self,
1095 localizations: impl IntoIterator<Item = (K, V)>,
1096 ) -> Self {
1097 self.0.description_localizations = Some(
1098 localizations
1099 .into_iter()
1100 .map(|(a, b)| (a.into(), b.into()))
1101 .collect(),
1102 );
1103
1104 self
1105 }
1106
1107 pub const fn max_length(mut self, value: u16) -> Self {
1111 self.0.max_length = Some(value);
1112
1113 self
1114 }
1115
1116 pub const fn min_length(mut self, value: u16) -> Self {
1120 self.0.min_length = Some(value);
1121
1122 self
1123 }
1124
1125 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1129 mut self,
1130 localizations: impl IntoIterator<Item = (K, V)>,
1131 ) -> Self {
1132 self.0.name_localizations = Some(
1133 localizations
1134 .into_iter()
1135 .map(|(a, b)| (a.into(), b.into()))
1136 .collect(),
1137 );
1138
1139 self
1140 }
1141
1142 pub const fn required(mut self, required: bool) -> Self {
1146 self.0.required = Some(required);
1147
1148 self
1149 }
1150}
1151
1152impl From<StringBuilder> for CommandOption {
1153 fn from(builder: StringBuilder) -> CommandOption {
1154 builder.build()
1155 }
1156}
1157
1158#[derive(Clone, Debug)]
1160#[must_use = "should be used in a command builder"]
1161pub struct SubCommandBuilder(CommandOption);
1162
1163impl SubCommandBuilder {
1164 #[must_use = "builders have no effect if unused"]
1166 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1167 Self(CommandOption {
1168 autocomplete: None,
1169 channel_types: None,
1170 choices: None,
1171 description: description.into(),
1172 description_localizations: None,
1173 kind: CommandOptionType::SubCommand,
1174 max_length: None,
1175 max_value: None,
1176 min_length: None,
1177 min_value: None,
1178 name: name.into(),
1179 name_localizations: None,
1180 options: Some(Vec::new()),
1181 required: None,
1182 })
1183 }
1184
1185 #[allow(clippy::missing_const_for_fn)]
1187 #[must_use = "should be used in a command builder"]
1188 pub fn build(self) -> CommandOption {
1189 self.0
1190 }
1191
1192 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1196 mut self,
1197 localizations: impl IntoIterator<Item = (K, V)>,
1198 ) -> Self {
1199 self.0.description_localizations = Some(
1200 localizations
1201 .into_iter()
1202 .map(|(a, b)| (a.into(), b.into()))
1203 .collect(),
1204 );
1205
1206 self
1207 }
1208
1209 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1213 mut self,
1214 localizations: impl IntoIterator<Item = (K, V)>,
1215 ) -> Self {
1216 self.0.name_localizations = Some(
1217 localizations
1218 .into_iter()
1219 .map(|(a, b)| (a.into(), b.into()))
1220 .collect(),
1221 );
1222
1223 self
1224 }
1225
1226 pub fn option(self, option: impl Into<CommandOption>) -> Self {
1230 self._option(option.into())
1231 }
1232
1233 fn _option(mut self, option: CommandOption) -> Self {
1234 self.0
1235 .options
1236 .as_mut()
1237 .expect("set to Some in `new`")
1238 .push(option);
1239
1240 self
1241 }
1242}
1243
1244impl From<SubCommandBuilder> for CommandOption {
1245 fn from(builder: SubCommandBuilder) -> CommandOption {
1246 builder.build()
1247 }
1248}
1249
1250#[derive(Clone, Debug)]
1252#[must_use = "should be used in a command builder"]
1253pub struct SubCommandGroupBuilder(CommandOption);
1254
1255impl SubCommandGroupBuilder {
1256 #[must_use = "builders have no effect if unused"]
1258 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1259 Self(CommandOption {
1260 autocomplete: None,
1261 channel_types: None,
1262 choices: None,
1263 description: description.into(),
1264 description_localizations: None,
1265 kind: CommandOptionType::SubCommandGroup,
1266 max_length: None,
1267 max_value: None,
1268 min_length: None,
1269 min_value: None,
1270 name: name.into(),
1271 name_localizations: None,
1272 options: Some(Vec::new()),
1273 required: None,
1274 })
1275 }
1276
1277 #[allow(clippy::missing_const_for_fn)]
1279 #[must_use = "should be used in a command builder"]
1280 pub fn build(self) -> CommandOption {
1281 self.0
1282 }
1283
1284 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1288 mut self,
1289 localizations: impl IntoIterator<Item = (K, V)>,
1290 ) -> Self {
1291 self.0.description_localizations = Some(
1292 localizations
1293 .into_iter()
1294 .map(|(a, b)| (a.into(), b.into()))
1295 .collect(),
1296 );
1297
1298 self
1299 }
1300
1301 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1305 mut self,
1306 localizations: impl IntoIterator<Item = (K, V)>,
1307 ) -> Self {
1308 self.0.name_localizations = Some(
1309 localizations
1310 .into_iter()
1311 .map(|(a, b)| (a.into(), b.into()))
1312 .collect(),
1313 );
1314
1315 self
1316 }
1317
1318 pub fn subcommands(mut self, subcommands: impl IntoIterator<Item = SubCommandBuilder>) -> Self {
1322 self.0.options = Some(subcommands.into_iter().map(Into::into).collect());
1323
1324 self
1325 }
1326}
1327
1328impl From<SubCommandGroupBuilder> for CommandOption {
1329 fn from(builder: SubCommandGroupBuilder) -> CommandOption {
1330 builder.build()
1331 }
1332}
1333
1334#[derive(Clone, Debug)]
1336#[must_use = "should be used in a command builder"]
1337pub struct UserBuilder(CommandOption);
1338
1339impl UserBuilder {
1340 #[must_use = "builders have no effect if unused"]
1342 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1343 Self(CommandOption {
1344 autocomplete: None,
1345 channel_types: None,
1346 choices: None,
1347 description: description.into(),
1348 description_localizations: None,
1349 kind: CommandOptionType::User,
1350 max_length: None,
1351 max_value: None,
1352 min_length: None,
1353 min_value: None,
1354 name: name.into(),
1355 name_localizations: None,
1356 options: None,
1357 required: None,
1358 })
1359 }
1360
1361 #[allow(clippy::missing_const_for_fn)]
1363 #[must_use = "should be used in a command builder"]
1364 pub fn build(self) -> CommandOption {
1365 self.0
1366 }
1367
1368 pub fn description_localizations<K: Into<String>, V: Into<String>>(
1372 mut self,
1373 localizations: impl IntoIterator<Item = (K, V)>,
1374 ) -> Self {
1375 self.0.description_localizations = Some(
1376 localizations
1377 .into_iter()
1378 .map(|(a, b)| (a.into(), b.into()))
1379 .collect(),
1380 );
1381
1382 self
1383 }
1384
1385 pub fn name_localizations<K: Into<String>, V: Into<String>>(
1389 mut self,
1390 localizations: impl IntoIterator<Item = (K, V)>,
1391 ) -> Self {
1392 self.0.name_localizations = Some(
1393 localizations
1394 .into_iter()
1395 .map(|(a, b)| (a.into(), b.into()))
1396 .collect(),
1397 );
1398
1399 self
1400 }
1401
1402 pub const fn required(mut self, required: bool) -> Self {
1406 self.0.required = Some(required);
1407
1408 self
1409 }
1410}
1411
1412impl From<UserBuilder> for CommandOption {
1413 fn from(builder: UserBuilder) -> CommandOption {
1414 builder.build()
1415 }
1416}
1417
1418#[cfg(test)]
1419mod tests {
1420 use super::*;
1421 use static_assertions::assert_impl_all;
1422 use std::fmt::Debug;
1423
1424 assert_impl_all!(AttachmentBuilder: Clone, Debug, Send, Sync);
1425 assert_impl_all!(CommandBuilder: Clone, Debug, Send, Sync);
1426 assert_impl_all!(BooleanBuilder: Clone, Debug, Send, Sync);
1427 assert_impl_all!(ChannelBuilder: Clone, Debug, Send, Sync);
1428 assert_impl_all!(IntegerBuilder: Clone, Debug, Send, Sync);
1429 assert_impl_all!(MentionableBuilder: Clone, Debug, Send, Sync);
1430 assert_impl_all!(RoleBuilder: Clone, Debug, Send, Sync);
1431 assert_impl_all!(StringBuilder: Clone, Debug, Send, Sync);
1432 assert_impl_all!(SubCommandBuilder: Clone, Debug, Send, Sync);
1433 assert_impl_all!(SubCommandGroupBuilder: Clone, Debug, Send, Sync);
1434 assert_impl_all!(UserBuilder: Clone, Debug, Send, Sync);
1435
1436 #[test]
1437 #[allow(clippy::too_many_lines, deprecated)]
1438 fn construct_command_with_builder() {
1439 let command =
1440 CommandBuilder::new(
1441 "permissions",
1442 "Get or edit permissions for a user or a role",
1443 CommandType::ChatInput,
1444 )
1445 .nsfw(true)
1446 .option(
1447 SubCommandGroupBuilder::new("user", "Get or edit permissions for a user")
1448 .subcommands([
1449 SubCommandBuilder::new("get", "Get permissions for a user")
1450 .option(UserBuilder::new("user", "The user to get").required(true))
1451 .option(ChannelBuilder::new(
1452 "channel",
1453 "The channel permissions to get. If omitted, the guild \
1454 permissions will be returned",
1455 )),
1456 SubCommandBuilder::new("edit", "Edit permissions for a user")
1457 .option(UserBuilder::new("user", "The user to edit").required(true))
1458 .option(ChannelBuilder::new(
1459 "channel",
1460 "The channel permissions to edit. If omitted, the guild \
1461 permissions will be edited",
1462 )),
1463 ]),
1464 )
1465 .option(
1466 SubCommandGroupBuilder::new("role", "Get or edit permissions for a role")
1467 .subcommands([
1468 SubCommandBuilder::new("get", "Get permissions for a role")
1469 .option(RoleBuilder::new("role", "The role to get").required(true))
1470 .option(ChannelBuilder::new(
1471 "channel",
1472 "The channel permissions to get. If omitted, the guild \
1473 permissions will be returned",
1474 )),
1475 SubCommandBuilder::new("edit", "Edit permissions for a role")
1476 .option(RoleBuilder::new("role", "The role to edit").required(true))
1477 .option(ChannelBuilder::new(
1478 "channel",
1479 "The channel permissions to edit. If omitted, the guild \
1480 permissions will be edited",
1481 )),
1482 ]),
1483 )
1484 .build();
1485
1486 let command_manual = Command {
1487 application_id: None,
1488 contexts: None,
1489 default_member_permissions: None,
1490 dm_permission: None,
1491 description: String::from("Get or edit permissions for a user or a role"),
1492 description_localizations: None,
1493 guild_id: None,
1494 id: None,
1495 integration_types: None,
1496 kind: CommandType::ChatInput,
1497 name: String::from("permissions"),
1498 name_localizations: None,
1499 nsfw: Some(true),
1500 options: Vec::from([
1501 CommandOption {
1502 autocomplete: None,
1503 channel_types: None,
1504 choices: None,
1505 description: "Get or edit permissions for a user".to_owned(),
1506 description_localizations: None,
1507 kind: CommandOptionType::SubCommandGroup,
1508 max_length: None,
1509 max_value: None,
1510 min_length: None,
1511 min_value: None,
1512 name: "user".to_owned(),
1513 name_localizations: None,
1514 options: Some(Vec::from([
1515 CommandOption {
1516 autocomplete: None,
1517 channel_types: None,
1518 choices: None,
1519 description: "Get permissions for a user".to_owned(),
1520 description_localizations: None,
1521 kind: CommandOptionType::SubCommand,
1522 max_length: None,
1523 max_value: None,
1524 min_length: None,
1525 min_value: None,
1526 name: "get".to_owned(),
1527 name_localizations: None,
1528 options: Some(Vec::from([
1529 CommandOption {
1530 autocomplete: None,
1531 channel_types: None,
1532 choices: None,
1533 description: "The user to get".to_owned(),
1534 description_localizations: None,
1535 kind: CommandOptionType::User,
1536 max_length: None,
1537 max_value: None,
1538 min_length: None,
1539 min_value: None,
1540 name: "user".to_owned(),
1541 name_localizations: None,
1542 options: None,
1543 required: Some(true),
1544 },
1545 CommandOption {
1546 autocomplete: None,
1547 channel_types: Some(Vec::new()),
1548 choices: None,
1549 description:
1550 "The channel permissions to get. If omitted, the guild \
1551 permissions will be returned"
1552 .to_owned(),
1553 description_localizations: None,
1554 kind: CommandOptionType::Channel,
1555 max_length: None,
1556 max_value: None,
1557 min_length: None,
1558 min_value: None,
1559 name: "channel".to_owned(),
1560 name_localizations: None,
1561 options: None,
1562 required: None,
1563 },
1564 ])),
1565 required: None,
1566 },
1567 CommandOption {
1568 autocomplete: None,
1569 channel_types: None,
1570 choices: None,
1571 description: "Edit permissions for a user".to_owned(),
1572 description_localizations: None,
1573 kind: CommandOptionType::SubCommand,
1574 max_length: None,
1575 max_value: None,
1576 min_length: None,
1577 min_value: None,
1578 name: "edit".to_owned(),
1579 name_localizations: None,
1580 options: Some(Vec::from([
1581 CommandOption {
1582 autocomplete: None,
1583 channel_types: None,
1584 choices: None,
1585 description: "The user to edit".to_owned(),
1586 description_localizations: None,
1587 kind: CommandOptionType::User,
1588 max_length: None,
1589 max_value: None,
1590 min_length: None,
1591 min_value: None,
1592 name: "user".to_owned(),
1593 name_localizations: None,
1594 options: None,
1595 required: Some(true),
1596 },
1597 CommandOption {
1598 autocomplete: None,
1599 channel_types: Some(Vec::new()),
1600 choices: None,
1601 description:
1602 "The channel permissions to edit. If omitted, the guild \
1603 permissions will be edited"
1604 .to_owned(),
1605 description_localizations: None,
1606 kind: CommandOptionType::Channel,
1607 max_length: None,
1608 max_value: None,
1609 min_length: None,
1610 min_value: None,
1611 name: "channel".to_owned(),
1612 name_localizations: None,
1613 options: None,
1614 required: None,
1615 },
1616 ])),
1617 required: None,
1618 },
1619 ])),
1620 required: None,
1621 },
1622 CommandOption {
1623 autocomplete: None,
1624 channel_types: None,
1625 choices: None,
1626 description: "Get or edit permissions for a role".to_owned(),
1627 description_localizations: None,
1628 kind: CommandOptionType::SubCommandGroup,
1629 max_length: None,
1630 max_value: None,
1631 min_length: None,
1632 min_value: None,
1633 name: "role".to_owned(),
1634 name_localizations: None,
1635 options: Some(Vec::from([
1636 CommandOption {
1637 autocomplete: None,
1638 channel_types: None,
1639 choices: None,
1640 description: "Get permissions for a role".to_owned(),
1641 description_localizations: None,
1642 kind: CommandOptionType::SubCommand,
1643 max_length: None,
1644 max_value: None,
1645 min_length: None,
1646 min_value: None,
1647 name: "get".to_owned(),
1648 name_localizations: None,
1649 options: Some(Vec::from([
1650 CommandOption {
1651 autocomplete: None,
1652 channel_types: None,
1653 choices: None,
1654 description: "The role to get".to_owned(),
1655 description_localizations: None,
1656 kind: CommandOptionType::Role,
1657 max_length: None,
1658 max_value: None,
1659 min_length: None,
1660 min_value: None,
1661 name: "role".to_owned(),
1662 name_localizations: None,
1663 options: None,
1664 required: Some(true),
1665 },
1666 CommandOption {
1667 autocomplete: None,
1668 channel_types: Some(Vec::new()),
1669 choices: None,
1670 description:
1671 "The channel permissions to get. If omitted, the guild \
1672 permissions will be returned"
1673 .to_owned(),
1674 description_localizations: None,
1675 kind: CommandOptionType::Channel,
1676 max_length: None,
1677 max_value: None,
1678 min_length: None,
1679 min_value: None,
1680 name: "channel".to_owned(),
1681 name_localizations: None,
1682 options: None,
1683 required: None,
1684 },
1685 ])),
1686 required: None,
1687 },
1688 CommandOption {
1689 autocomplete: None,
1690 channel_types: None,
1691 choices: None,
1692 description: "Edit permissions for a role".to_owned(),
1693 description_localizations: None,
1694 kind: CommandOptionType::SubCommand,
1695 max_length: None,
1696 max_value: None,
1697 min_length: None,
1698 min_value: None,
1699 name: "edit".to_owned(),
1700 name_localizations: None,
1701 options: Some(Vec::from([
1702 CommandOption {
1703 autocomplete: None,
1704 channel_types: None,
1705 choices: None,
1706 description: "The role to edit".to_owned(),
1707 description_localizations: None,
1708 kind: CommandOptionType::Role,
1709 max_length: None,
1710 max_value: None,
1711 min_length: None,
1712 min_value: None,
1713 name: "role".to_owned(),
1714 name_localizations: None,
1715 options: None,
1716 required: Some(true),
1717 },
1718 CommandOption {
1719 autocomplete: None,
1720 channel_types: Some(Vec::new()),
1721 choices: None,
1722 description:
1723 "The channel permissions to edit. If omitted, the guild \
1724 permissions will be edited"
1725 .to_owned(),
1726 description_localizations: None,
1727 kind: CommandOptionType::Channel,
1728 max_length: None,
1729 max_value: None,
1730 min_length: None,
1731 min_value: None,
1732 name: "channel".to_owned(),
1733 name_localizations: None,
1734 options: None,
1735 required: None,
1736 },
1737 ])),
1738 required: None,
1739 },
1740 ])),
1741 required: None,
1742 },
1743 ]),
1744 version: Id::new(1),
1745 };
1746
1747 assert_eq!(command, command_manual);
1748 }
1749
1750 #[test]
1751 fn validate() {
1752 let result = CommandBuilder::new("", "", CommandType::ChatInput).validate();
1753
1754 assert!(result.is_err());
1755 }
1756}