use crate::cli_error::{CliError, CliErrorKind};
use crate::command::Command;
use std::collections::HashMap;
use std::env;
use std::iter::Peekable;
pub fn parse_command_line() -> Result<Command, CliError> {
let command_line_arguments: Vec<String> = env::args().skip(1).collect::<Vec<String>>();
let mut command_line_arguments = command_line_arguments.iter().map(|s| &s[..]).peekable();
parse_command_line_args(&mut command_line_arguments)
}
pub fn parse_command_string(string: &str) -> Result<Command, CliError> {
parse_command_line_args(&mut string.split(" ").into_iter().peekable())
}
pub fn parse_command_line_args<'a, I>(args: &mut Peekable<I>) -> Result<Command, CliError>
where
I: Iterator<Item = &'a str>,
{
let mut arguments = HashMap::new();
let command = match args.peek() {
None => "",
Some(&value) => {
if !value.starts_with("-") {
args.next();
value
} else {
""
}
}
};
loop {
match parse_command_argument(args)? {
None => break,
Some((name, values)) => {
arguments.insert(name, values);
}
}
}
Ok(Command {
name: command.to_string(),
arguments,
})
}
fn parse_command_argument<'a, I>(
args: &mut Peekable<I>,
) -> Result<Option<(String, Box<[String]>)>, CliError>
where
I: Iterator<Item = &'a str>,
{
match args.next() {
None => Ok(None),
Some(ref value) if value.starts_with("-") => {
let values = parse_argument_values(args);
let key_index: usize = if value.starts_with("--") { 2 } else { 1 };
Ok(Some(((&value[key_index..]).to_string(), values)))
}
Some(_) => Err(CliErrorKind::ParseCommandLine.into()),
}
}
fn parse_argument_values<'a, I>(args: &mut Peekable<I>) -> Box<[String]>
where
I: Iterator<Item = &'a str>,
{
let mut result = vec![];
loop {
match args.peek() {
Some(&value) => {
if !value.starts_with("-") {
result.push(args.next().unwrap().to_string());
} else {
break;
}
}
_ => break,
}
}
result.into_boxed_slice()
}