1use crate::cli_error::{CliError, CliErrorKind};
2use crate::command::Command;
3use std::collections::HashMap;
4use std::env;
5use std::iter::Peekable;
6
7pub fn parse_command_line() -> Result<Command, CliError> {
27 let command_line_arguments: Vec<String> = env::args().skip(1).collect::<Vec<String>>();
28 let mut command_line_arguments = command_line_arguments.iter().map(|s| &s[..]).peekable();
29 parse_command_line_args(&mut command_line_arguments)
30}
31
32pub fn parse_command_string(string: &str) -> Result<Command, CliError> {
54 parse_command_line_args(&mut string.split(" ").into_iter().peekable())
55}
56
57pub fn parse_command_line_args<'a, I>(args: &mut Peekable<I>) -> Result<Command, CliError>
58where
59 I: Iterator<Item = &'a str>,
60{
61 let mut arguments = HashMap::new();
62 let command = match args.peek() {
63 None => "",
64 Some(&value) => {
65 if !value.starts_with("-") {
66 args.next();
67 value
68 } else {
69 ""
70 }
71 }
72 };
73
74 loop {
75 match parse_command_argument(args)? {
76 None => break,
77 Some((name, values)) => {
78 arguments.insert(name, values);
79 }
80 }
81 }
82 Ok(Command {
83 name: command.to_string(),
84 arguments,
85 })
86}
87
88fn parse_command_argument<'a, I>(
89 args: &mut Peekable<I>,
90) -> Result<Option<(String, Box<[String]>)>, CliError>
91where
92 I: Iterator<Item = &'a str>,
93{
94 match args.next() {
95 None => Ok(None),
96 Some(ref value) if value.starts_with("-") => {
97 let values = parse_argument_values(args);
98 let key_index: usize = if value.starts_with("--") { 2 } else { 1 };
99 Ok(Some(((&value[key_index..]).to_string(), values)))
100 }
101 Some(_) => Err(CliErrorKind::ParseCommandLine.into()),
102 }
103}
104
105fn parse_argument_values<'a, I>(args: &mut Peekable<I>) -> Box<[String]>
106where
107 I: Iterator<Item = &'a str>,
108{
109 let mut result = vec![];
110 loop {
111 match args.peek() {
112 Some(&value) => {
113 if !value.starts_with("-") {
114 result.push(args.next().unwrap().to_string());
115 } else {
116 break;
117 }
118 }
119 _ => break,
120 }
121 }
122
123 result.into_boxed_slice()
124}