1mod errors;
12
13pub use crate::errors::{Error, Result};
14use serenity::model::channel::PartialChannel;
15use serenity::model::guild::{PartialMember, Role};
16use serenity::model::interactions::application_command::{
17 ApplicationCommandInteractionData, ApplicationCommandInteractionDataOptionValue,
18 ApplicationCommandOptionType,
19};
20use serenity::model::misc::{Mention, Mentionable as SerenityMentionable};
21use serenity::model::user::User;
22use std::collections::HashMap;
23
24#[derive(Debug)]
26pub struct SlashValue {
27 inner: Option<ApplicationCommandInteractionDataOptionValue>,
29 name: String,
31}
32
33pub enum UserOrMember {
35 User(User),
36 Member(User, PartialMember),
37}
38
39impl UserOrMember {
40 fn from_pair(user: User, member: Option<PartialMember>) -> Self {
41 match member {
42 Some(m) => Self::Member(user, m),
43 None => Self::User(user),
44 }
45 }
46
47 pub fn get_user(&self) -> &User {
49 match self {
50 UserOrMember::User(s) => s,
51 UserOrMember::Member(u, _) => u,
52 }
53 }
54
55 pub fn get_member(&self) -> Option<&PartialMember> {
57 match self {
58 UserOrMember::User(_) => None,
59 UserOrMember::Member(_, m) => Some(m),
60 }
61 }
62}
63
64pub enum Mentionable {
66 UserOrMember(UserOrMember),
67 Role(Role),
68}
69
70impl SerenityMentionable for Mentionable {
71 fn mention(&self) -> Mention {
72 match self {
73 Mentionable::UserOrMember(u) => u.get_user().mention(),
74 Mentionable::Role(r) => r.mention(),
75 }
76 }
77}
78
79impl SlashValue {
80 fn get_type_name(&self) -> String {
81 match self.inner.as_ref().unwrap() {
82 ApplicationCommandInteractionDataOptionValue::String(_) => "String".to_string(),
83 ApplicationCommandInteractionDataOptionValue::Integer(_) => "Integer".to_string(),
84 ApplicationCommandInteractionDataOptionValue::Boolean(_) => "Boolean".to_string(),
85 ApplicationCommandInteractionDataOptionValue::User(_, _) => "User".to_string(),
86 ApplicationCommandInteractionDataOptionValue::Channel(_) => "Channel".to_string(),
87 ApplicationCommandInteractionDataOptionValue::Role(_) => "Role".to_string(),
88 _ => "Unknown".to_string(),
89 }
90 }
91
92 pub fn expect_some(&self) -> Result<ApplicationCommandInteractionDataOptionValue> {
94 match self.inner.clone() {
95 Some(s) => Ok(s),
96 None => Err(Error::MissingValue {
97 name: self.name.clone(),
98 }),
99 }
100 }
101
102 pub fn get_string(&self) -> Result<String> {
104 match self.expect_some()? {
105 ApplicationCommandInteractionDataOptionValue::String(s) => Ok(s),
106 _ => Err(Error::WrongType {
107 expected: "String".to_string(),
108 found: self.get_type_name(),
109 name: self.name.clone(),
110 }),
111 }
112 }
113
114 pub fn get_integer(&self) -> Result<i64> {
116 match self.expect_some()? {
117 ApplicationCommandInteractionDataOptionValue::Integer(s) => Ok(s),
118 _ => Err(Error::WrongType {
119 expected: "Integer".to_string(),
120 found: self.get_type_name(),
121 name: self.name.clone(),
122 }),
123 }
124 }
125
126 pub fn get_boolean(&self) -> Result<bool> {
128 match self.expect_some()? {
129 ApplicationCommandInteractionDataOptionValue::Boolean(s) => Ok(s),
130 _ => Err(Error::WrongType {
131 expected: "Boolean".to_string(),
132 found: self.get_type_name(),
133 name: self.name.clone(),
134 }),
135 }
136 }
137
138 pub fn get_user(&self) -> Result<UserOrMember> {
140 match self.expect_some()? {
141 ApplicationCommandInteractionDataOptionValue::User(u, m) => {
142 Ok(UserOrMember::from_pair(u, m))
143 }
144 _ => Err(Error::WrongType {
145 expected: "User".to_string(),
146 found: self.get_type_name(),
147 name: self.name.clone(),
148 }),
149 }
150 }
151
152 pub fn get_channel(&self) -> Result<PartialChannel> {
154 match self.expect_some()? {
155 ApplicationCommandInteractionDataOptionValue::Channel(s) => Ok(s),
156 _ => Err(Error::WrongType {
157 expected: "Channel".to_string(),
158 found: self.get_type_name(),
159 name: self.name.clone(),
160 }),
161 }
162 }
163
164 pub fn get_role(&self) -> Result<Role> {
166 match self.expect_some()? {
167 ApplicationCommandInteractionDataOptionValue::Role(s) => Ok(s),
168 _ => Err(Error::WrongType {
169 expected: "Role".to_string(),
170 found: self.get_type_name(),
171 name: self.name.clone(),
172 }),
173 }
174 }
175
176 pub fn get_mentionable(&self) -> Result<Mentionable> {
178 match self.expect_some()? {
179 ApplicationCommandInteractionDataOptionValue::User(u, m) => {
180 Ok(Mentionable::UserOrMember(UserOrMember::from_pair(u, m)))
181 }
182 ApplicationCommandInteractionDataOptionValue::Role(r) => Ok(Mentionable::Role(r)),
183 _ => Err(Error::WrongType {
184 expected: "Mentionable".to_string(),
185 found: self.get_type_name(),
186 name: self.name.clone(),
187 }),
188 }
189 }
190}
191
192pub struct SlashMap(HashMap<String, SlashValue>);
194
195impl SlashMap {
196 fn new() -> Self {
197 Self(HashMap::new())
198 }
199
200 pub fn get_string(&self, name: &str) -> Result<String> {
202 match self.0.get(name) {
203 Some(s) => s.get_string(),
204 None => Err(Error::MissingValue {
205 name: name.to_string(),
206 }),
207 }
208 }
209
210 pub fn get_integer(&self, name: &str) -> Result<i64> {
212 match self.0.get(name) {
213 Some(s) => s.get_integer(),
214 None => Err(Error::MissingValue {
215 name: name.to_string(),
216 }),
217 }
218 }
219
220 pub fn get_boolean(&self, name: &str) -> Result<bool> {
222 match self.0.get(name) {
223 Some(s) => s.get_boolean(),
224 None => Err(Error::MissingValue {
225 name: name.to_string(),
226 }),
227 }
228 }
229
230 pub fn get_user(&self, name: &str) -> Result<UserOrMember> {
232 match self.0.get(name) {
233 Some(s) => s.get_user(),
234 None => Err(Error::MissingValue {
235 name: name.to_string(),
236 }),
237 }
238 }
239
240 pub fn get_channel(&self, name: &str) -> Result<PartialChannel> {
242 match self.0.get(name) {
243 Some(s) => s.get_channel(),
244 None => Err(Error::MissingValue {
245 name: name.to_string(),
246 }),
247 }
248 }
249
250 pub fn get_role(&self, name: &str) -> Result<Role> {
252 match self.0.get(name) {
253 Some(s) => s.get_role(),
254 None => Err(Error::MissingValue {
255 name: name.to_string(),
256 }),
257 }
258 }
259
260 pub fn get_mentionable(&self, name: &str) -> Result<Mentionable> {
262 match self.0.get(name) {
263 Some(s) => s.get_mentionable(),
264 None => Err(Error::MissingValue {
265 name: name.to_string(),
266 }),
267 }
268 }
269}
270
271pub trait FromSlashMap {
273 fn from_slash_map(_: SlashMap) -> Result<Self>
274 where
275 Self: Sized;
276}
277
278pub fn process(interaction: &ApplicationCommandInteractionData) -> (String, SlashMap) {
280 let mut options = &interaction.options;
282 let mut path = Vec::new();
283 path.push(interaction.name.clone());
284
285 loop {
286 match options.get(0) {
287 None => break,
288 Some(option) => {
289 if matches!(
290 option.kind,
291 ApplicationCommandOptionType::SubCommand
292 | ApplicationCommandOptionType::SubCommandGroup
293 ) {
294 path.push(option.name.clone());
295 options = &option.options;
296 } else {
297 break;
298 }
299 }
300 }
301 }
302
303 let mut map = SlashMap::new();
305 for option in options {
306 map.0.insert(
307 option.name.clone(),
308 SlashValue {
309 inner: option.resolved.clone(),
310 name: option.name.clone(),
311 },
312 );
313 }
314
315 (path.join(" "), map)
316}