1use std::collections::HashMap;
6
7pub struct ArgumentParser {
8 name: String,
9 args: HashMap<String, (Argument, bool, usize)>,
10}
11
12#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
13pub enum ArgumentType {
14 Boolean,
15 Integer,
16 Float,
17 Double,
18 String,
19}
20
21#[derive(Debug, Clone, PartialEq, PartialOrd)]
22pub enum ArgumentValue {
23 None,
24 Boolean(bool),
25 Integer(i64),
26 Float(f32),
27 Double(f64),
28 String(String),
29}
30
31impl Into<String> for ArgumentValue {
32 fn into(self) -> String {
33 return match self {
34 ArgumentValue::String(val) => val,
35 _ => panic!("Cannot convert {:?} into a String.", self),
36 };
37 }
38}
39
40impl Into<i64> for ArgumentValue {
41 fn into(self) -> i64 {
42 return match self {
43 ArgumentValue::Integer(val) => val,
44 ArgumentValue::Float(val) => val as i64,
45 ArgumentValue::Double(val) => val as i64,
46 _ => panic!("Cannot convert {:?} into an i64", self),
47 };
48 }
49}
50
51impl Into<f32> for ArgumentValue {
52 fn into(self) -> f32 {
53 return match self {
54 ArgumentValue::Float(val) => val,
55 _ => panic!("Cannot convert {:?} into a f32", self),
56 };
57 }
58}
59
60impl Into<f64> for ArgumentValue {
61 fn into(self) -> f64 {
62 return match self {
63 ArgumentValue::Double(val) => val,
64 ArgumentValue::Float(val) => val as f64,
65 ArgumentValue::Integer(val) => val as f64,
66 _ => panic!("Cannot convert {:?} into a f64", self),
67 };
68 }
69}
70
71#[derive(Debug)]
72pub struct Argument {
73 name: String,
74 description: String,
75 required: bool,
76 arg_type: ArgumentType,
77 default_value: ArgumentValue,
78}
79
80impl Argument {
81 pub fn new(name: &str) -> Self {
83 return Argument {
84 name: name.to_string(),
85 description: String::new(),
86 required: false,
87 arg_type: ArgumentType::String,
88 default_value: ArgumentValue::None,
89 };
90 }
91
92 pub fn is_required(mut self, required: bool) -> Self {
94 self.required = required;
95 return self;
96 }
97
98 pub fn with_type(mut self, arg_type: ArgumentType) -> Self {
100 self.arg_type = arg_type;
101 return self;
102 }
103
104 pub fn with_description(mut self, description: &str) -> Self {
106 self.description = description.to_string();
107 return self;
108 }
109
110 pub fn with_default_value(mut self, value: ArgumentValue) -> Self {
112 match (self.arg_type, value.clone()) {
113 (ArgumentType::String, ArgumentValue::String(_))
114 | (ArgumentType::Integer, ArgumentValue::Integer(_))
115 | (ArgumentType::Float, ArgumentValue::Float(_))
116 | (ArgumentType::Double, ArgumentValue::Double(_)) => {
117 self.default_value = value;
118 }
119 (_, _) => panic!(
120 "Argument type: {:?} is incompatible with default value: {:?}",
121 self.arg_type, value
122 ),
123 }
124
125 return self;
126 }
127
128 pub fn arg_type(&self) -> ArgumentType {
129 return self.arg_type.clone();
130 }
131
132 pub fn name(&self) -> &str {
133 return self.name.as_ref();
134 }
135
136 pub fn default_value(&self) -> ArgumentValue {
137 return self.default_value.clone();
138 }
139
140 pub fn description(&self) -> &str {
141 return self.description.as_ref();
142 }
143}
144
145#[derive(Debug, Clone)]
146pub struct ParsedArgument {
147 name: String,
148 value: ArgumentValue,
149}
150
151impl ParsedArgument {
152 fn new(name: &str, value: ArgumentValue) -> Self {
153 return ParsedArgument {
154 name: name.to_string(),
155 value,
156 };
157 }
158
159 pub fn name(&self) -> String {
160 return self.name.clone();
161 }
162
163 pub fn value(&self) -> ArgumentValue {
164 return self.value.clone();
165 }
166}
167
168impl ArgumentParser {
169 pub fn new(name: &str) -> Self {
171 return ArgumentParser {
172 name: name.to_string(),
173 args: HashMap::new(),
174 };
175 }
176
177 pub fn name(&self) -> &str {
179 return self.name.as_ref();
180 }
181
182 pub fn argument_count(&self) -> usize {
184 return self.args.len();
185 }
186
187 pub fn with_author(mut self, author: &str) {
188 todo!("Implement adding authors to a command line parser")
189 }
190
191 pub fn with_description(mut self, description: &str) {
193 todo!("Implement adding a description to the command line parser.")
194 }
195
196 pub fn with_argument(mut self, argument: Argument) -> Self {
197 self.args.insert(
198 argument.name().to_string(),
199 (argument, false, self.args.len()),
200 );
201 return self;
202 }
203
204 pub fn compile(mut self, args: &[String]) -> Vec<ParsedArgument> {
212 let mut collecting_values = false;
213 let mut last_argument: Option<&mut (Argument, bool, usize)> = None;
214
215 let mut parsed_arguments = vec![];
216 parsed_arguments.resize(
217 self.args.len(),
218 ParsedArgument::new("", ArgumentValue::None),
219 );
220
221 for arg in args.into_iter().skip(1) {
222 if collecting_values {
223 let (arg_ref, found, index) = last_argument.as_mut().unwrap();
224
225 let parsed_value = match arg_ref.arg_type() {
226 ArgumentType::String => ArgumentValue::String(arg.clone()),
227 ArgumentType::Float => {
228 ArgumentValue::Float(arg.parse().unwrap_or_else(|err| {
229 panic!(
230 "Could not convert {:?} to a float because of: {}",
231 arg, err
232 )
233 }))
234 }
235 ArgumentType::Double => {
236 ArgumentValue::Double(arg.parse().unwrap_or_else(|err| {
237 panic!(
238 "Could not convert {:?} to a double because of: {}",
239 arg, err
240 )
241 }))
242 }
243 ArgumentType::Integer => {
244 ArgumentValue::Integer(arg.parse().unwrap_or_else(|err| {
245 panic!(
246 "Could not convert {:?} to an integer because of: {}",
247 arg, err
248 )
249 }))
250 }
251 ArgumentType::Boolean => {
252 ArgumentValue::Boolean(arg.parse().unwrap_or_else(|err| {
253 panic!(
254 "Could not convert {:?} to a boolean because of: {}",
255 arg, err
256 )
257 }))
258 }
259 };
260
261 parsed_arguments[*index] =
262 ParsedArgument::new(arg_ref.name.as_str(), parsed_value);
263
264 collecting_values = false;
265 *found = true;
266 continue;
267 }
268
269 let found_argument = self.args.get_mut(arg).unwrap_or_else(|| {
272 panic!("Argument: {} is not a valid argument", &arg)
273 });
274
275 if found_argument.1 == true {
277 panic!("{} was set more than once.", found_argument.0.name.clone());
278 }
279
280 collecting_values = true;
281 last_argument = Some(found_argument);
282 }
283
284 for (arg, found, index) in self.args.values() {
287 match (arg.required, found, arg.default_value.clone()) {
288 (true, false, _) => panic!(
290 "--{} is a required argument, but was not found.",
291 arg.name.clone()
292 ),
293 (false, false, value) => {
295 parsed_arguments[*index] =
296 ParsedArgument::new(arg.name.as_str(), value);
297 }
298 (_, _, _) => {}
300 }
301 }
302 return parsed_arguments;
303 }
304}