use crate::*;
use std::io::{self, Write};
pub fn input(prompt: &str) -> String {
print!("{}", prompt);
io::stdout().flush().expect("Failed to flush stdout");
let mut buffer = String::new();
io::stdin()
.read_line(&mut buffer)
.expect("Failed to read line");
buffer.trim_end().to_string()
}
pub fn format_flag(name: &str, alias: Option<&str>) -> String {
if let Some(alias) = alias {
format!("--{}, -{}", name, alias)
} else {
format!("--{}", name)
}
}
pub fn parse_flags<S: AsRef<str>>(args: &[S]) -> std::collections::HashMap<String, String> {
let mut flags = std::collections::HashMap::new();
let args: Vec<&str> = args.iter().map(|arg| arg.as_ref()).collect();
let mut i = 0;
while i < args.len() {
let arg = args[i];
if arg.starts_with("-") && !arg.starts_with("---") {
let flag_name = if let Some(stripped) = arg.strip_prefix("--") {
stripped.to_string()
} else {
arg[1..].to_string()
};
if arg.starts_with("--") {
if i + 1 < args.len() && !args[i + 1].starts_with("-") {
flags.insert(flag_name, args[i + 1].to_string());
i += 2;
} else {
flags.insert(flag_name, "true".to_string());
i += 1;
}
} else {
flags.insert(flag_name, "true".to_string());
i += 1;
}
} else {
i += 1;
}
}
flags
}
pub(crate) fn dispatch(
command: &Command,
args: &[String],
flags: std::collections::HashMap<String, String>,
global_flags: &[Flag],
prog: &str,
) {
let next = args.iter().find(|a| !a.starts_with('-'));
if let Some(name) = next
&& let Some(sub) = command.subcommands.iter().find(|s| s.name == *name)
{
let mut sub_flags = flags.clone();
for (key, value) in flags.iter() {
let canonical = sub
.known_flags
.iter()
.chain(global_flags.iter())
.find(|f| f.alias.as_deref() == Some(key.as_str()))
.map(|f| f.name.clone())
.unwrap_or_else(|| key.clone());
sub_flags.insert(canonical, value.clone());
}
return dispatch(sub, &args[1..], sub_flags, global_flags, prog);
}
if next.is_none() {
if command.handler.is_some() && command.print_help_if_no_args {
eprintln!(
"warning: handler and print_help_if_no_args are mutually exclusive. Handler takes priority."
);
}
if command.handler.is_none() {
if command.print_help_if_no_args {
command.print_help(prog);
return;
} else {
println!("error: No subcommand provided.");
return;
}
}
}
for parsed_flag in flags.keys() {
if parsed_flag == "help" || parsed_flag == "version" {
continue;
}
let is_known = command
.known_flags
.iter()
.chain(global_flags.iter())
.any(|f| f.name == *parsed_flag);
if !is_known {
if command.strict_flags {
println!(
"error: Unknown flag '--{}' for command '{}'.",
parsed_flag, command.name
);
return;
}
println!("warning: Unknown flag '--{}'.", parsed_flag);
}
}
let positionals: Vec<String> = {
let mut result = Vec::new();
let mut skip_next = false;
for arg in args.iter() {
if skip_next {
skip_next = false;
continue;
}
if let Some(key) = arg.strip_prefix("--") {
if let Some(val) = flags.get(key)
&& val != "true"
{
skip_next = true;
}
continue;
}
if arg.starts_with('-') {
continue;
}
result.push(arg.clone());
}
result
};
if let Some(handler) = command.handler {
handler(&CommandContext {
subcommand: command.name.clone(),
positionals,
flags,
});
}
}