use std::collections::HashMap;
use std::env;
#[derive(Debug, Clone)]
pub struct Args {
pub command: String,
pub positional: Vec<String>,
pub flags: HashMap<String, bool>,
pub options: HashMap<String, String>,
}
impl Args {
pub fn parse() -> Self {
let mut args: Vec<String> = env::args().collect();
let command = if !args.is_empty() {
args.remove(0)
} else {
String::new()
};
let mut positional = Vec::new();
let mut flags = HashMap::new();
let mut options = HashMap::new();
let mut i = 0;
while i < args.len() {
let arg = &args[i];
let consumed =
Self::parse_argument(arg, &args, i, &mut flags, &mut options, &mut positional);
i += consumed;
}
Args {
command,
positional,
flags,
options,
}
}
fn parse_argument(
arg: &str,
args: &[String],
index: usize,
flags: &mut HashMap<String, bool>,
options: &mut HashMap<String, String>,
positional: &mut Vec<String>,
) -> usize {
if let Some(without_prefix) = arg.strip_prefix("--") {
Self::parse_long_argument(without_prefix, args, index, flags, options)
} else if let Some(without_prefix) = arg.strip_prefix('-') {
Self::parse_short_argument(without_prefix, arg, args, index, flags, options, positional)
} else {
positional.push(arg.to_string());
1
}
}
fn parse_long_argument(
without_prefix: &str,
args: &[String],
index: usize,
flags: &mut HashMap<String, bool>,
options: &mut HashMap<String, String>,
) -> usize {
if without_prefix.contains('=') {
let parts: Vec<&str> = without_prefix.splitn(2, '=').collect();
options.insert(parts[0].to_string(), parts[1].to_string());
1
} else if index + 1 < args.len() && !args[index + 1].starts_with('-') {
options.insert(without_prefix.to_string(), args[index + 1].clone());
2 } else {
flags.insert(without_prefix.to_string(), true);
1
}
}
fn parse_short_argument(
without_prefix: &str,
arg: &str,
args: &[String],
index: usize,
flags: &mut HashMap<String, bool>,
options: &mut HashMap<String, String>,
positional: &mut Vec<String>,
) -> usize {
if without_prefix.is_empty() {
positional.push(arg.to_string());
return 1;
}
let mut consumed = 1;
for (j, c) in without_prefix.chars().enumerate() {
let flag_name = c.to_string();
let is_last_char = j == without_prefix.len() - 1;
let has_next_value = index + 1 < args.len() && !args[index + 1].starts_with('-');
if is_last_char && has_next_value {
options.insert(flag_name, args[index + 1].clone());
consumed = 2;
break;
} else {
flags.insert(flag_name, true);
}
}
consumed
}
pub fn has_flag(&self, name: &str) -> bool {
self.flags.get(name).copied().unwrap_or(false)
}
pub fn get_option(&self, name: &str) -> Option<&String> {
self.options.get(name)
}
}