twilight_model/application/command/option.rs
1use crate::channel::ChannelType;
2use serde::{Deserialize, Serialize};
3use serde_repr::{Deserialize_repr, Serialize_repr};
4use std::{cmp::Eq, collections::HashMap};
5
6/// Option for a [`Command`].
7///
8/// Fields not applicable to the command option's [`CommandOptionType`] should
9/// be set to [`None`].
10///
11/// Fields' default values may be used by setting them to [`None`].
12///
13/// Choices, descriptions and names may be localized in any [available locale],
14/// see [Discord Docs/Localization].
15///
16/// [available locale]: https://discord.com/developers/docs/reference#locales
17/// [`Command`]: super::Command
18/// [Discord Docs/Localization]: https://discord.com/developers/docs/interactions/application-commands#localization
19#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
20pub struct CommandOption {
21 /// Whether the command supports autocomplete.
22 ///
23 /// Applicable for options of type [`Integer`], [`Number`], and [`String`].
24 ///
25 /// Defaults to `false`.
26 ///
27 /// **Note**: may not be set to `true` if `choices` are set.
28 ///
29 /// [`Integer`]: CommandOptionType::Integer
30 /// [`Number`]: CommandOptionType::Number
31 /// [`String`]: CommandOptionType::String
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub autocomplete: Option<bool>,
34 /// List of possible channel types users can select from.
35 ///
36 /// Applicable for options of type [`Channel`].
37 ///
38 /// Defaults to any channel type.
39 ///
40 /// [`Channel`]: CommandOptionType::Channel
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub channel_types: Option<Vec<ChannelType>>,
43 /// List of predetermined choices users can select from.
44 ///
45 /// Applicable for options of type [`Integer`], [`Number`], and [`String`].
46 ///
47 /// Defaults to no choices; users may input a value of their choice.
48 ///
49 /// Must be at most 25 options.
50 ///
51 /// **Note**: all choices must be of the same type.
52 ///
53 /// [`Integer`]: CommandOptionType::Integer
54 /// [`Number`]: CommandOptionType::Number
55 /// [`String`]: CommandOptionType::String
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub choices: Option<Vec<CommandOptionChoice>>,
58 /// Description of the option. Must be 100 characters or less.
59 pub description: String,
60 /// Localization dictionary for the [`description`] field.
61 ///
62 /// Defaults to no localizations.
63 ///
64 /// Keys must be valid locales and values must be 100 characters or less.
65 ///
66 /// [`description`]: Self::description
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub description_localizations: Option<HashMap<String, String>>,
69 /// Type of option.
70 #[serde(rename = "type")]
71 pub kind: CommandOptionType,
72 /// Maximum allowed value length.
73 ///
74 /// Applicable for options of type [`String`].
75 ///
76 /// Defaults to `6000`.
77 ///
78 /// Must be at least `1` and at most `6000`.
79 ///
80 /// [`String`]: CommandOptionType::String
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub max_length: Option<u16>,
83 /// Maximum allowed value.
84 ///
85 /// Applicable for options of type [`Integer`] and [`Number`].
86 ///
87 /// Defaults to no maximum.
88 ///
89 /// [`Integer`]: CommandOptionType::Integer
90 /// [`Number`]: CommandOptionType::Number
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub max_value: Option<CommandOptionValue>,
93 /// Minimum allowed value length.
94 ///
95 /// Applicable for options of type [`String`].
96 ///
97 /// Defaults to `0`.
98 ///
99 /// Must be at most `6000`.
100 ///
101 /// [`String`]: CommandOptionType::String
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub min_length: Option<u16>,
104 /// Minimum allowed value.
105 ///
106 /// Applicable for options of type [`Integer`] and [`Number`].
107 ///
108 /// Defaults to no minimum.
109 ///
110 /// [`Integer`]: CommandOptionType::Integer
111 /// [`Number`]: CommandOptionType::Number
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub min_value: Option<CommandOptionValue>,
114 /// Name of the option. Must be 32 characters or less.
115 pub name: String,
116 /// Localization dictionary for the [`name`] field.
117 ///
118 /// Defaults to no localizations.
119 ///
120 /// Keys must be valid locales and values must be 32 characters or less.
121 ///
122 /// [`name`]: Self::name
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub name_localizations: Option<HashMap<String, String>>,
125 /// Nested options.
126 ///
127 /// Applicable for options of type [`SubCommand`] and [`SubCommandGroup`].
128 ///
129 /// Defaults to no options.
130 ///
131 /// **Note**: at least one option is required and [`SubCommandGroup`] may
132 /// only contain [`SubCommand`]s.
133 ///
134 /// See [Discord Docs/Subcommands and Subcommand Groups].
135 ///
136 /// [Discord Docs/Subcommands and Subcommand Groups]: https://discord.com/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups
137 /// [`SubCommand`]: CommandOptionType::SubCommand
138 /// [`SubCommandGroup`]: CommandOptionType::SubCommandGroup
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub options: Option<Vec<CommandOption>>,
141 /// Whether the option is required.
142 ///
143 /// Applicable for all options except those of type [`SubCommand`] and
144 /// [`SubCommandGroup`].
145 ///
146 /// Defaults to `false`.
147 ///
148 /// [`SubCommand`]: CommandOptionType::SubCommand
149 /// [`SubCommandGroup`]: CommandOptionType::SubCommandGroup
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub required: Option<bool>,
152}
153
154/// A predetermined choice users can select.
155#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
156pub struct CommandOptionChoice {
157 /// Name of the choice. Must be 100 characters or less.
158 pub name: String,
159 /// Localization dictionary for the [`name`] field.
160 ///
161 /// Defaults to no localizations.
162 ///
163 /// Keys must be valid locales and values must be 100 characters or less.
164 ///
165 /// See [`CommandOption`]'s documentation for more info.
166 ///
167 /// [`name`]: Self::name
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub name_localizations: Option<HashMap<String, String>>,
170 /// Value of the choice.
171 pub value: CommandOptionChoiceValue,
172}
173
174/// Value of a [`CommandOptionChoice`].
175///
176/// Note that the right variant must be selected based on the
177/// [`CommandOption`]'s [`CommandOptionType`].
178#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
179#[serde(untagged)]
180pub enum CommandOptionChoiceValue {
181 /// String choice. Must be 100 characters or less.
182 String(String),
183 /// Integer choice.
184 Integer(i64),
185 /// Number choice.
186 Number(f64),
187}
188
189/// Type used in the `max_value` and `min_value` [`CommandOption`] field.
190///
191/// Note that the right variant must be selected based on the
192/// [`CommandOption`]'s [`CommandOptionType`].
193#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
194#[serde(untagged)]
195pub enum CommandOptionValue {
196 /// Integer type.
197 Integer(i64),
198 /// Number type.
199 Number(f64),
200}
201
202/// Type of a [`CommandOption`].
203#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)]
204#[non_exhaustive]
205#[repr(u8)]
206pub enum CommandOptionType {
207 SubCommand = 1,
208 SubCommandGroup = 2,
209 String = 3,
210 Integer = 4,
211 Boolean = 5,
212 User = 6,
213 Channel = 7,
214 Role = 8,
215 Mentionable = 9,
216 Number = 10,
217 Attachment = 11,
218}
219
220impl CommandOptionType {
221 pub const fn kind(self) -> &'static str {
222 match self {
223 CommandOptionType::SubCommand => "SubCommand",
224 CommandOptionType::SubCommandGroup => "SubCommandGroup",
225 CommandOptionType::String => "String",
226 CommandOptionType::Integer => "Integer",
227 CommandOptionType::Boolean => "Boolean",
228 CommandOptionType::User => "User",
229 CommandOptionType::Channel => "Channel",
230 CommandOptionType::Role => "Role",
231 CommandOptionType::Mentionable => "Mentionable",
232 CommandOptionType::Number => "Number",
233 CommandOptionType::Attachment => "Attachment",
234 }
235 }
236}