modcli/
args.rs

1//! Lightweight argument helper functions for common patterns.
2//!
3//! Supports forms like:
4//! --flag
5//! --flag=true|false
6//! --key value
7//! --key=value
8
9use crate::error::ModCliError;
10
11/// Return true if a boolean flag is present or set to true.
12/// Matches `--flag`, `--flag=true`, `--flag=1`, `--flag=yes` (case-insensitive).
13pub fn flag(args: &[String], name: &str) -> bool {
14    if !name.starts_with("--") {
15        return false;
16    }
17    let key = name.trim_start_matches('-');
18    for a in args {
19        let a = a.as_str();
20        if a == name {
21            return true;
22        }
23        if let Some((k, v)) = a.strip_prefix("--").and_then(|s| s.split_once('=')) {
24            if k == key {
25                let v = v.to_ascii_lowercase();
26                return matches!(v.as_str(), "1" | "true" | "yes" | "y");
27            }
28        }
29    }
30    false
31}
32
33/// Get a string value for `--key` from either `--key value` or `--key=value`.
34pub fn get_string(args: &[String], name: &str) -> Option<String> {
35    if !name.starts_with("--") {
36        return None;
37    }
38    let key = name.trim_start_matches('-');
39    let mut i = 0;
40    while i < args.len() {
41        let a = args[i].as_str();
42        if a == name {
43            if let Some(next) = args.get(i + 1) {
44                return Some(next.clone());
45            }
46            return None;
47        }
48        if let Some((k, v)) = a.strip_prefix("--").and_then(|s| s.split_once('=')) {
49            if k == key {
50                return Some(v.to_string());
51            }
52        }
53        i += 1;
54    }
55    None
56}
57
58/// Get an integer value for `--key`.
59/// Returns ModCliError::InvalidUsage on parse failure.
60pub fn get_int<T>(args: &[String], name: &str) -> Result<T, ModCliError>
61where
62    T: std::str::FromStr,
63{
64    if let Some(s) = get_string(args, name) {
65        match s.parse::<T>() {
66            Ok(v) => Ok(v),
67            Err(_) => Err(ModCliError::InvalidUsage(format!(
68                "expected numeric value for {name}, got '{s}'"
69            ))),
70        }
71    } else {
72        Err(ModCliError::InvalidUsage(format!(
73            "missing required argument: {name}"
74        )))
75    }
76}
77
78/// Get a boolean value for `--key`.
79/// If the key is present with no value (i.e., `--key`), returns true.
80/// If provided as `--key=value`, parses truthy/falsey strings.
81pub fn get_bool(args: &[String], name: &str) -> Result<bool, ModCliError> {
82    if flag(args, name) {
83        return Ok(true);
84    }
85    if let Some(s) = get_string(args, name) {
86        let v = s.to_ascii_lowercase();
87        return Ok(matches!(v.as_str(), "1" | "true" | "yes" | "y"));
88    }
89    Err(ModCliError::InvalidUsage(format!(
90        "missing required argument: {name}"
91    )))
92}