cli_command/command.rs
1use crate::cli_error::{CliError, CliErrorKind};
2use std::collections::HashMap;
3
4/// Represents a parsed command with its name and arguments.
5///
6/// The `Command` struct holds the parsed command-line arguments in a structured format.
7/// It provides convenient methods for accessing arguments with type conversion and error handling.
8///
9/// # Examples
10///
11/// ```rust
12/// use cli_command::{Command, parse_command_string};
13///
14/// let cmd = parse_command_string("serve --port 8080 --host localhost").unwrap();
15/// assert_eq!(cmd.name, "serve");
16/// assert_eq!(cmd.get_argument("port"), Some("8080"));
17/// assert_eq!(cmd.get_argument("host"), Some("localhost"));
18/// ```
19#[derive(Debug, Clone)]
20pub struct Command {
21 /// The command name (first non-flag argument)
22 pub name: String,
23 /// Parsed arguments as a map of argument names to their values
24 pub arguments: HashMap<String, Box<[String]>>,
25}
26
27impl Command {
28 /// Gets the nth parameter of an argument as a `usize`, if it exists and can be parsed.
29 ///
30 /// # Arguments
31 /// * `argument` - The name of the argument
32 /// * `nth` - The zero-based index of the parameter
33 ///
34 /// # Returns
35 /// * `Some(usize)` - If the parameter exists and can be parsed as a usize
36 /// * `None` - If the argument doesn't exist or the parameter can't be parsed
37 ///
38 /// # Examples
39 /// ```rust
40 /// use cli_command::{Command, parse_command_string};
41 ///
42 /// let cmd = parse_command_string("--numbers 1 2 3").unwrap();
43 /// assert_eq!(cmd.get_argument_nth_parameter_usize("numbers", 0), Some(1));
44 /// assert_eq!(cmd.get_argument_nth_parameter_usize("numbers", 1), Some(2));
45 /// assert_eq!(cmd.get_argument_nth_parameter_usize("numbers", 5), None);
46 /// ```
47 pub fn get_argument_nth_parameter_usize(&self, argument: &str, nth: usize) -> Option<usize> {
48 match self.arguments.get(argument) {
49 Some(params) if params.len() > nth => usize::from_str_radix(¶ms[nth], 10).ok(),
50 _ => None,
51 }
52 }
53
54 /// Gets the nth parameter of an argument as a string slice, if it exists.
55 ///
56 /// # Arguments
57 /// * `argument` - The name of the argument
58 /// * `nth` - The zero-based index of the parameter
59 ///
60 /// # Returns
61 /// * `Some(&str)` - If the parameter exists
62 /// * `None` - If the argument doesn't exist or the parameter is out of bounds
63 ///
64 /// # Examples
65 /// ```rust
66 /// use cli_command::{Command, parse_command_string};
67 ///
68 /// let cmd = parse_command_string("--files a.txt b.txt c.txt").unwrap();
69 /// assert_eq!(cmd.get_argument_nth_parameter("files", 0), Some("a.txt"));
70 /// assert_eq!(cmd.get_argument_nth_parameter("files", 1), Some("b.txt"));
71 /// assert_eq!(cmd.get_argument_nth_parameter("files", 5), None);
72 /// ```
73 pub fn get_argument_nth_parameter(&self, argument: &str, nth: usize) -> Option<&str> {
74 match self.arguments.get(argument) {
75 Some(params) if params.len() > nth => Some(¶ms[nth]),
76 _ => None,
77 }
78 }
79
80 /// Gets the nth parameter of an argument as a string slice, returning an error if missing.
81 ///
82 /// # Arguments
83 /// * `argument` - The name of the argument
84 /// * `nth` - The zero-based index of the parameter
85 ///
86 /// # Returns
87 /// * `Ok(&str)` - If the parameter exists
88 /// * `Err(CliError)` - If the argument or parameter is missing
89 ///
90 /// # Examples
91 /// ```rust
92 /// use cli_command::{Command, parse_command_string};
93 ///
94 /// let cmd = parse_command_string("--files a.txt b.txt").unwrap();
95 /// assert_eq!(cmd.get_argument_nth_param_mandatory("files", 0).unwrap(), "a.txt");
96 /// assert_eq!(cmd.get_argument_nth_param_mandatory("files", 1).unwrap(), "b.txt");
97 /// ```
98 pub fn get_argument_nth_param_mandatory(
99 &self,
100 argument: &str,
101 nth: usize,
102 ) -> Result<&str, CliError> {
103 let params = self.get_argument_mandatory_all(argument)?;
104 if params.len() > nth {
105 Ok(¶ms[nth])
106 } else if nth > 0 {
107 Err(CliErrorKind::MissingParameter(argument.to_string(), nth).into())
108 } else {
109 Err(CliErrorKind::MissingArgument(argument.to_string()).into())
110 }
111 }
112
113 /// Gets the nth parameter of an argument as a `usize`, returning an error if missing or invalid.
114 ///
115 /// # Arguments
116 /// * `argument` - The name of the argument
117 /// * `nth` - The zero-based index of the parameter
118 ///
119 /// # Returns
120 /// * `Ok(usize)` - If the parameter exists and can be parsed
121 /// * `Err(CliError)` - If the argument is missing or the parameter can't be parsed
122 ///
123 /// # Examples
124 /// ```rust
125 /// use cli_command::{Command, parse_command_string};
126 ///
127 /// let cmd = parse_command_string("--numbers 1 2 3").unwrap();
128 /// assert_eq!(cmd.get_argument_nth_param_mandatory_usize("numbers", 0).unwrap(), 1);
129 /// assert_eq!(cmd.get_argument_nth_param_mandatory_usize("numbers", 1).unwrap(), 2);
130 /// ```
131 pub fn get_argument_nth_param_mandatory_usize(
132 &self,
133 argument: &str,
134 nth: usize,
135 ) -> Result<usize, CliError> {
136 let param = self.get_argument_nth_param_mandatory(argument, nth)?;
137 Ok(usize::from_str_radix(param, 10)?)
138 }
139
140 /// Checks if an argument exists (regardless of whether it has values).
141 ///
142 /// # Arguments
143 /// * `argument` - The name of the argument to check
144 ///
145 /// # Returns
146 /// * `true` - If the argument exists
147 /// * `false` - If the argument doesn't exist
148 ///
149 /// # Examples
150 /// ```rust
151 /// use cli_command::{Command, parse_command_string};
152 ///
153 /// let cmd = parse_command_string("--verbose --port 8080").unwrap();
154 /// assert!(cmd.contains_argument("verbose"));
155 /// assert!(cmd.contains_argument("port"));
156 /// assert!(!cmd.contains_argument("debug"));
157 /// ```
158 pub fn contains_argument(&self, argument: &str) -> bool {
159 self.arguments.contains_key(argument)
160 }
161
162 /// Gets the first parameter of an argument as a string slice, if it exists.
163 ///
164 /// # Arguments
165 /// * `argument` - The name of the argument
166 ///
167 /// # Returns
168 /// * `Some(&str)` - If the argument exists and has at least one parameter
169 /// * `None` - If the argument doesn't exist or has no parameters
170 ///
171 /// # Examples
172 /// ```rust
173 /// use cli_command::{Command, parse_command_string};
174 ///
175 /// let cmd = parse_command_string("--host localhost --port 8080").unwrap();
176 /// assert_eq!(cmd.get_argument("host"), Some("localhost"));
177 /// assert_eq!(cmd.get_argument("port"), Some("8080"));
178 /// assert_eq!(cmd.get_argument("debug"), None);
179 /// ```
180 pub fn get_argument(&self, argument: &str) -> Option<&str> {
181 self.get_argument_nth_parameter(argument, 0)
182 }
183
184 /// Gets the first parameter of an argument as a string slice, returning an error if missing.
185 ///
186 /// # Arguments
187 /// * `argument` - The name of the argument
188 ///
189 /// # Returns
190 /// * `Ok(&str)` - If the argument exists and has at least one parameter
191 /// * `Err(CliError)` - If the argument is missing
192 ///
193 /// # Examples
194 /// ```rust
195 /// use cli_command::{Command, parse_command_string};
196 ///
197 /// let cmd = parse_command_string("--host localhost").unwrap();
198 /// assert_eq!(cmd.get_argument_mandatory("host").unwrap(), "localhost");
199 /// ```
200 pub fn get_argument_mandatory(&self, argument: &str) -> Result<&str, CliError> {
201 self.get_argument_nth_param_mandatory(argument, 0)
202 }
203
204 /// Gets the first parameter of an argument as a `usize`, returning an error if missing or invalid.
205 ///
206 /// # Arguments
207 /// * `argument` - The name of the argument
208 ///
209 /// # Returns
210 /// * `Ok(usize)` - If the argument exists and can be parsed as a usize
211 /// * `Err(CliError)` - If the argument is missing or can't be parsed
212 ///
213 /// # Examples
214 /// ```rust
215 /// use cli_command::{Command, parse_command_string};
216 ///
217 /// let cmd = parse_command_string("--port 8080").unwrap();
218 /// assert_eq!(cmd.get_argument_mandatory_usize("port").unwrap(), 8080);
219 /// ```
220 pub fn get_argument_mandatory_usize(&self, argument: &str) -> Result<usize, CliError> {
221 let value = self.get_argument_mandatory(argument)?;
222 Ok(usize::from_str_radix(value, 10)?)
223 }
224
225 /// Gets the first parameter of an argument as a `usize`, if it exists and can be parsed.
226 ///
227 /// # Arguments
228 /// * `argument` - The name of the argument
229 ///
230 /// # Returns
231 /// * `Some(usize)` - If the argument exists and can be parsed as a usize
232 /// * `None` - If the argument doesn't exist or can't be parsed
233 ///
234 /// # Examples
235 /// ```rust
236 /// use cli_command::{Command, parse_command_string};
237 ///
238 /// let cmd = parse_command_string("--port 8080 --workers 4").unwrap();
239 /// assert_eq!(cmd.get_argument_usize("port"), Some(8080));
240 /// assert_eq!(cmd.get_argument_usize("workers"), Some(4));
241 /// assert_eq!(cmd.get_argument_usize("debug"), None);
242 /// ```
243 pub fn get_argument_usize(&self, argument: &str) -> Option<usize> {
244 match self.get_argument(argument) {
245 Some(v) => match usize::from_str_radix(v, 10) {
246 Ok(v) => Some(v),
247 Err(_) => None,
248 },
249 None => None,
250 }
251 }
252
253 /// Gets the first parameter of an argument as an `i32`, if it exists and can be parsed.
254 ///
255 /// # Arguments
256 /// * `argument` - The name of the argument
257 ///
258 /// # Returns
259 /// * `Some(i32)` - If the argument exists and can be parsed as an i32
260 /// * `None` - If the argument doesn't exist or can't be parsed
261 ///
262 /// # Examples
263 /// ```rust
264 /// use cli_command::{Command, parse_command_string};
265 ///
266 /// let cmd = parse_command_string("--port 8080 --timeout 30").unwrap();
267 /// assert_eq!(cmd.get_argument_i32("port"), Some(8080));
268 /// assert_eq!(cmd.get_argument_i32("timeout"), Some(30));
269 /// assert_eq!(cmd.get_argument_i32("debug"), None);
270 /// ```
271 pub fn get_argument_i32(&self, argument: &str) -> Option<i32> {
272 match self.get_argument(argument) {
273 Some(v) => v.parse().ok(),
274 None => None,
275 }
276 }
277
278 /// Gets the first parameter of an argument as a `f64`, if it exists and can be parsed.
279 ///
280 /// # Arguments
281 /// * `argument` - The name of the argument
282 ///
283 /// # Returns
284 /// * `Some(f64)` - If the argument exists and can be parsed as an f64
285 /// * `None` - If the argument doesn't exist or can't be parsed
286 ///
287 /// # Examples
288 /// ```rust
289 /// use cli_command::{Command, parse_command_string};
290 ///
291 /// let cmd = parse_command_string("--ratio 0.5 --threshold 1.23").unwrap();
292 /// assert_eq!(cmd.get_argument_f64("ratio"), Some(0.5));
293 /// assert_eq!(cmd.get_argument_f64("threshold"), Some(1.23));
294 /// assert_eq!(cmd.get_argument_f64("debug"), None);
295 /// ```
296 pub fn get_argument_f64(&self, argument: &str) -> Option<f64> {
297 match self.get_argument(argument) {
298 Some(v) => v.parse().ok(),
299 None => None,
300 }
301 }
302
303 /// Gets the first parameter of an argument as a `bool`, if it exists and can be parsed.
304 ///
305 /// # Arguments
306 /// * `argument` - The name of the argument
307 ///
308 /// # Returns
309 /// * `Some(bool)` - If the argument exists and can be parsed as a bool
310 /// * `None` - If the argument doesn't exist or can't be parsed
311 ///
312 /// # Examples
313 /// ```rust
314 /// use cli_command::{Command, parse_command_string};
315 ///
316 /// let cmd = parse_command_string("--enabled true --disabled false").unwrap();
317 /// assert_eq!(cmd.get_argument_bool("enabled"), Some(true));
318 /// assert_eq!(cmd.get_argument_bool("disabled"), Some(false));
319 /// assert_eq!(cmd.get_argument_bool("debug"), None);
320 /// ```
321 pub fn get_argument_bool(&self, argument: &str) -> Option<bool> {
322 match self.get_argument(argument) {
323 Some(v) => v.parse().ok(),
324 None => None,
325 }
326 }
327
328 /// Gets all parameters of an argument as a slice of strings, if it exists.
329 ///
330 /// # Arguments
331 /// * `argument` - The name of the argument
332 ///
333 /// # Returns
334 /// * `Some(&Box<[String]>)` - If the argument exists
335 /// * `None` - If the argument doesn't exist
336 ///
337 /// # Examples
338 /// ```rust
339 /// use cli_command::{Command, parse_command_string};
340 ///
341 /// let cmd = parse_command_string("--files a.txt b.txt c.txt").unwrap();
342 /// let files = cmd.get_argument_all("files").unwrap();
343 /// assert_eq!(files.len(), 3);
344 /// assert_eq!(&files[0], "a.txt");
345 /// assert_eq!(&files[1], "b.txt");
346 /// assert_eq!(&files[2], "c.txt");
347 /// ```
348 pub fn get_argument_all(&self, argument: &str) -> Option<&Box<[String]>> {
349 self.arguments.get(argument)
350 }
351
352 /// Gets all parameters of an argument as a slice of strings, returning an error if missing.
353 ///
354 /// # Arguments
355 /// * `argument` - The name of the argument
356 ///
357 /// # Returns
358 /// * `Ok(&Box<[String]>)` - If the argument exists
359 /// * `Err(CliError)` - If the argument is missing
360 ///
361 /// # Examples
362 /// ```rust
363 /// use cli_command::{Command, parse_command_string};
364 ///
365 /// let cmd = parse_command_string("--files a.txt b.txt").unwrap();
366 /// let files = cmd.get_argument_mandatory_all("files").unwrap();
367 /// assert_eq!(files.len(), 2);
368 /// ```
369 pub fn get_argument_mandatory_all(&self, argument: &str) -> Result<&Box<[String]>, CliError> {
370 self.get_argument_all(argument)
371 .ok_or(CliErrorKind::MissingArgument(argument.to_string()).into())
372 }
373
374 /// Gets the first parameter of an argument with type conversion, or returns a default value if missing.
375 ///
376 /// This method attempts to parse the argument value as the specified type `T`. If the argument
377 /// is missing, it returns the provided default value instead of an error.
378 ///
379 /// # Arguments
380 /// * `argument` - The name of the argument
381 /// * `default` - The default value to return if the argument is missing
382 ///
383 /// # Returns
384 /// * `Ok(T)` - If the argument exists and can be parsed, or if missing (returns default)
385 /// * `Err(CliError)` - If the argument exists but can't be parsed as the target type
386 ///
387 /// # Examples
388 /// ```rust
389 /// use cli_command::{Command, parse_command_string};
390 ///
391 /// let cmd = parse_command_string("--port 8080").unwrap();
392 /// let port: u16 = cmd.get_argument_or_default("port", 3000).unwrap();
393 /// assert_eq!(port, 8080);
394 ///
395 /// let timeout: u64 = cmd.get_argument_or_default("timeout", 30).unwrap();
396 /// assert_eq!(timeout, 30); // Uses default since timeout not provided
397 /// ```
398 pub fn get_argument_or_default<T>(&self, argument: &str, default: T) -> Result<T, CliError>
399 where
400 T: std::str::FromStr,
401 T::Err: std::error::Error + Send + Sync + 'static,
402 {
403 match self.get_argument_mandatory(argument) {
404 Ok(value) => T::from_str(value).map_err(|e| CliError::new_inner(Box::new(e))),
405 Err(_) => Ok(default),
406 }
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use crate::cli_error::{CliError, CliErrorKind};
413 use crate::parse::parse_command_string;
414 use std::net::SocketAddr;
415 use std::str::FromStr;
416
417 #[test]
418 fn test_get_argument_first_value_or_default() -> Result<(), CliError> {
419 let cmd = parse_command_string("--socket 172.20.3.1:7618")?;
420 assert_eq!(&cmd.arguments.get("socket").unwrap()[0], "172.20.3.1:7618");
421
422 let socket =
423 cmd.get_argument_or_default("socket", SocketAddr::from_str("127.0.0.1:1000")?)?;
424 assert_eq!(socket, SocketAddr::from_str("172.20.3.1:7618")?);
425
426 let cmd = parse_command_string("--streams 2")?;
427 let streams = cmd.get_argument_or_default("streams", 5)?;
428 assert_eq!(streams, 2);
429 assert_eq!(cmd.get_argument_or_default("components", 5)?, 5);
430
431 Ok(())
432 }
433
434 #[test]
435 fn test_basic_argument_parsing() -> Result<(), CliError> {
436 let cmd = parse_command_string("serve --port 8080 --host localhost --verbose")?;
437
438 assert_eq!(cmd.name, "serve");
439 assert_eq!(cmd.get_argument("port"), Some("8080"));
440 assert_eq!(cmd.get_argument("host"), Some("localhost"));
441 assert!(cmd.contains_argument("verbose"));
442 assert!(!cmd.contains_argument("debug"));
443
444 Ok(())
445 }
446
447 #[test]
448 fn test_multiple_values() -> Result<(), CliError> {
449 let cmd = parse_command_string("build --files a.txt b.txt c.txt --output dist/")?;
450
451 assert_eq!(cmd.name, "build");
452 let files = cmd.get_argument_all("files").unwrap();
453 assert_eq!(files.len(), 3);
454 assert_eq!(&files[0], "a.txt");
455 assert_eq!(&files[1], "b.txt");
456 assert_eq!(&files[2], "c.txt");
457 assert_eq!(cmd.get_argument("output"), Some("dist/"));
458
459 Ok(())
460 }
461
462 #[test]
463 fn test_usize_conversion() -> Result<(), CliError> {
464 let cmd = parse_command_string("--port 8080 --workers 4 --timeout 30")?;
465
466 assert_eq!(cmd.get_argument_usize("port"), Some(8080));
467 assert_eq!(cmd.get_argument_usize("workers"), Some(4));
468 assert_eq!(cmd.get_argument_usize("timeout"), Some(30));
469 assert_eq!(cmd.get_argument_usize("missing"), None);
470
471 Ok(())
472 }
473
474 #[test]
475 fn test_mandatory_arguments() -> Result<(), CliError> {
476 let cmd = parse_command_string("--required value")?;
477
478 assert_eq!(cmd.get_argument_mandatory("required")?, "value");
479
480 let cmd = parse_command_string("")?;
481 let result = cmd.get_argument_mandatory("missing");
482 assert!(result.is_err());
483
484 if let Err(CliError {
485 kind: CliErrorKind::MissingArgument(arg),
486 ..
487 }) = result
488 {
489 assert_eq!(arg, "missing");
490 } else {
491 panic!("Expected MissingArgument error");
492 }
493
494 Ok(())
495 }
496
497 #[test]
498 fn test_nth_parameter_access() -> Result<(), CliError> {
499 let cmd = parse_command_string("--numbers 1 2 3 4 5")?;
500
501 assert_eq!(cmd.get_argument_nth_parameter("numbers", 0), Some("1"));
502 assert_eq!(cmd.get_argument_nth_parameter("numbers", 2), Some("3"));
503 assert_eq!(cmd.get_argument_nth_parameter("numbers", 4), Some("5"));
504 assert_eq!(cmd.get_argument_nth_parameter("numbers", 5), None);
505 assert_eq!(cmd.get_argument_nth_parameter("missing", 0), None);
506
507 Ok(())
508 }
509
510 #[test]
511 fn test_nth_parameter_usize() -> Result<(), CliError> {
512 let cmd = parse_command_string("--values 10 20 30")?;
513
514 assert_eq!(cmd.get_argument_nth_parameter_usize("values", 0), Some(10));
515 assert_eq!(cmd.get_argument_nth_parameter_usize("values", 1), Some(20));
516 assert_eq!(cmd.get_argument_nth_parameter_usize("values", 2), Some(30));
517 assert_eq!(cmd.get_argument_nth_parameter_usize("values", 3), None);
518 assert_eq!(cmd.get_argument_nth_parameter_usize("missing", 0), None);
519
520 Ok(())
521 }
522
523 #[test]
524 fn test_mandatory_nth_parameter() -> Result<(), CliError> {
525 let cmd = parse_command_string("--files a.txt b.txt c.txt")?;
526
527 assert_eq!(cmd.get_argument_nth_param_mandatory("files", 0)?, "a.txt");
528 assert_eq!(cmd.get_argument_nth_param_mandatory("files", 1)?, "b.txt");
529 assert_eq!(cmd.get_argument_nth_param_mandatory("files", 2)?, "c.txt");
530
531 let result = cmd.get_argument_nth_param_mandatory("files", 5);
532 assert!(result.is_err());
533
534 if let Err(CliError {
535 kind: CliErrorKind::MissingParameter(arg, pos),
536 ..
537 }) = result
538 {
539 assert_eq!(arg, "files");
540 assert_eq!(pos, 5);
541 } else {
542 panic!("Expected MissingParameter error");
543 }
544
545 Ok(())
546 }
547
548 #[test]
549 fn test_mandatory_nth_parameter_usize() -> Result<(), CliError> {
550 let cmd = parse_command_string("--numbers 1 2 3")?;
551
552 assert_eq!(cmd.get_argument_nth_param_mandatory_usize("numbers", 0)?, 1);
553 assert_eq!(cmd.get_argument_nth_param_mandatory_usize("numbers", 1)?, 2);
554 assert_eq!(cmd.get_argument_nth_param_mandatory_usize("numbers", 2)?, 3);
555
556 Ok(())
557 }
558
559 #[test]
560 fn test_type_conversion_with_defaults() -> Result<(), CliError> {
561 let cmd = parse_command_string("--port 8080")?;
562
563 let port: u16 = cmd.get_argument_or_default("port", 3000)?;
564 assert_eq!(port, 8080);
565
566 let timeout: u64 = cmd.get_argument_or_default("timeout", 30)?;
567 assert_eq!(timeout, 30); // Uses default
568
569 let host: String = cmd.get_argument_or_default("host", "localhost".to_string())?;
570 assert_eq!(host, "localhost"); // Uses default
571
572 Ok(())
573 }
574
575 #[test]
576 fn test_short_and_long_arguments() -> Result<(), CliError> {
577 let cmd = parse_command_string("-v --verbose --port 8080 -h localhost")?;
578
579 assert!(cmd.contains_argument("v"));
580 assert!(cmd.contains_argument("verbose"));
581 assert_eq!(cmd.get_argument("port"), Some("8080"));
582 assert_eq!(cmd.get_argument("h"), Some("localhost"));
583
584 Ok(())
585 }
586
587 #[test]
588 fn test_boolean_flags() -> Result<(), CliError> {
589 let cmd = parse_command_string("--enable-feature --no-cache --debug")?;
590
591 assert!(cmd.contains_argument("enable-feature"));
592 assert!(cmd.contains_argument("no-cache"));
593 assert!(cmd.contains_argument("debug"));
594 assert!(!cmd.contains_argument("missing"));
595
596 Ok(())
597 }
598
599 #[test]
600 fn test_empty_command() -> Result<(), CliError> {
601 let cmd = parse_command_string("")?;
602
603 assert_eq!(cmd.name, "");
604 assert!(cmd.arguments.is_empty());
605
606 Ok(())
607 }
608
609 #[test]
610 fn test_command_without_arguments() -> Result<(), CliError> {
611 let cmd = parse_command_string("help")?;
612
613 assert_eq!(cmd.name, "help");
614 assert!(cmd.arguments.is_empty());
615
616 Ok(())
617 }
618
619 #[test]
620 fn test_invalid_usize_parsing() -> Result<(), CliError> {
621 let cmd = parse_command_string("--port abc --workers 123")?;
622
623 assert_eq!(cmd.get_argument_usize("port"), None); // Invalid number
624 assert_eq!(cmd.get_argument_usize("workers"), Some(123)); // Valid number
625 assert_eq!(cmd.get_argument_usize("missing"), None); // Missing argument
626
627 Ok(())
628 }
629
630 #[test]
631 fn test_error_handling() -> Result<(), CliError> {
632 let cmd = parse_command_string("--port 8080")?;
633
634 let result = cmd.get_argument_mandatory("missing");
635 assert!(result.is_err());
636
637 let result = cmd.get_argument_nth_param_mandatory("port", 5);
638 assert!(result.is_err());
639
640 Ok(())
641 }
642}