pikadick_slash_framework/
convert.rs1use crate::{
2 ArgumentParam,
3 BuilderError,
4};
5use serenity::model::application::{
6 CommandDataOptionValue,
7 CommandInteraction,
8};
9
10#[derive(Debug, thiserror::Error)]
12pub enum ConvertError {
13 #[error("unexpected type for '{name}', expected '{expected}', got '{actual:?}'")]
15 UnexpectedType {
16 name: &'static str,
18 expected: DataType,
20 actual: Option<DataType>,
24 },
25
26 #[error("missing required field for '{name}', expected '{expected}'")]
28 MissingRequiredField {
29 name: &'static str,
31 expected: DataType,
33 },
34}
35
36pub trait FromOptions: std::fmt::Debug + Send
38where
39 Self: Sized,
40{
41 fn from_options(interaction: &CommandInteraction) -> Result<Self, ConvertError>;
43
44 fn get_argument_params() -> Result<Vec<ArgumentParam>, BuilderError> {
46 Ok(Vec::new())
47 }
48}
49
50impl FromOptions for () {
52 fn from_options(_interaction: &CommandInteraction) -> Result<Self, ConvertError> {
53 Ok(())
54 }
55}
56
57#[derive(Debug, Copy, Clone)]
59pub enum DataType {
60 String,
62
63 Integer,
65
66 Boolean,
68}
69
70impl DataType {
71 pub fn as_str(self) -> &'static str {
73 match self {
74 Self::String => "String",
75 Self::Integer => "i64",
76 Self::Boolean => "bool",
77 }
78 }
79
80 pub fn from_data_option_value(v: &CommandDataOptionValue) -> Option<Self> {
84 match v {
85 CommandDataOptionValue::String(_) => Some(DataType::String),
86 CommandDataOptionValue::Integer(_) => Some(DataType::Integer),
87 CommandDataOptionValue::Boolean(_) => Some(DataType::Boolean),
88 _ => None,
89 }
90 }
91}
92
93impl std::fmt::Display for DataType {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 self.as_str().fmt(f)
96 }
97}
98
99pub trait FromOptionValue: Sized {
101 fn from_option_value(
103 name: &'static str,
104 option: &CommandDataOptionValue,
105 ) -> Result<Self, ConvertError>;
106
107 fn get_expected_data_type() -> DataType;
109
110 fn get_missing_default() -> Option<Self> {
115 None
116 }
117}
118
119impl FromOptionValue for bool {
120 fn from_option_value(
121 name: &'static str,
122 option: &CommandDataOptionValue,
123 ) -> Result<Self, ConvertError> {
124 let expected = Self::get_expected_data_type();
125
126 match option {
127 CommandDataOptionValue::Boolean(b) => Ok(*b),
128 t => Err(ConvertError::UnexpectedType {
129 name,
130 expected,
131 actual: DataType::from_data_option_value(t),
132 }),
133 }
134 }
135
136 fn get_expected_data_type() -> DataType {
137 DataType::Boolean
138 }
139}
140
141impl FromOptionValue for String {
142 fn from_option_value(
143 name: &'static str,
144 option: &CommandDataOptionValue,
145 ) -> Result<Self, ConvertError> {
146 let expected = Self::get_expected_data_type();
147
148 match option {
149 CommandDataOptionValue::String(s) => Ok(s.clone()),
150 t => Err(ConvertError::UnexpectedType {
151 name,
152 expected,
153 actual: DataType::from_data_option_value(t),
154 }),
155 }
156 }
157
158 fn get_expected_data_type() -> DataType {
159 DataType::String
160 }
161}
162
163impl<T> FromOptionValue for Option<T>
164where
165 T: FromOptionValue,
166{
167 fn from_option_value(
168 name: &'static str,
169 option: &CommandDataOptionValue,
170 ) -> Result<Self, ConvertError> {
171 T::from_option_value(name, option).map(Some)
172 }
173
174 fn get_missing_default() -> Option<Self> {
175 Some(None)
176 }
177
178 fn get_expected_data_type() -> DataType {
179 T::get_expected_data_type()
180 }
181}