1use crate::output_type::OutputType;
2const ARG_INFO_LONG: &str = "--info";
3const ARG_INFO_SHORT: &str = "-i";
4const ARG_PATH_LONG: &str = "--path";
5const ARG_PATH_SHORT: &str = "-p";
6const ARG_NO_PROGRESS_LONG: &str = "--no-progress";
7const ARG_NO_PROGRESS_SHORT: &str = "-n";
8const ARG_OUTPUT_LONG: &str = "--output";
9const ARG_OUTPUT_SHORT: &str = "-o";
10const ARG_VERSION_LONG: &str = "--version";
11const ARG_VERSION_SHORT: &str = "-v";
12
13pub struct ApplicationArguments {
14 pub is_need_to_show_help: bool,
15 pub is_need_to_show_progress: bool,
16 pub is_need_to_show_version: bool,
17 pub output_type: Option<OutputType>,
18 pub path: Option<String>,
19}
20
21pub fn parse_arguments(arguments: &Vec<String>) -> ApplicationArguments {
22 let is_need_to_show_help = get_is_need_to_show_help(&arguments);
23 let is_need_to_show_progress = get_is_need_to_show_progress(arguments);
24 let is_need_to_show_version = get_is_need_to_show_version(&arguments);
25 let output_type = get_output_type(arguments);
26 let path = get_path(arguments);
27
28 ApplicationArguments {
29 is_need_to_show_help,
30 is_need_to_show_progress,
31 is_need_to_show_version,
32 output_type,
33 path,
34 }
35}
36
37fn get_is_need_to_show_help(arguments: &Vec<String>) -> bool {
38 any(arguments, ARG_INFO_LONG, ARG_INFO_SHORT)
39}
40
41fn get_is_need_to_show_progress(arguments: &Vec<String>) -> bool {
42 !any(arguments, ARG_NO_PROGRESS_LONG, ARG_NO_PROGRESS_SHORT)
43}
44
45fn get_is_need_to_show_version(arguments: &Vec<String>) -> bool {
46 any(arguments, ARG_VERSION_LONG, ARG_VERSION_SHORT)
47}
48
49fn any(arguments: &Vec<String>, long_arg: &str, short_arg: &str) -> bool {
50 arguments.iter().any(|a| a == long_arg || a == short_arg)
51}
52
53fn get_output_type(arguments: &Vec<String>) -> Option<OutputType> {
54 let argument_value = get_argument_value(arguments, ARG_OUTPUT_LONG, ARG_OUTPUT_SHORT);
55
56 if let Some(argument_value) = argument_value {
57 if argument_value == "=json" {
58 return Some(OutputType::Json);
59 }
60 if argument_value == "=plain" {
61 return Some(OutputType::Plain);
62 }
63
64 return None;
65 }
66
67 return Some(OutputType::Plain);
68}
69
70fn get_path(arguments: &Vec<String>) -> Option<String> {
71 let argument_value = get_argument_value(arguments, ARG_PATH_LONG, ARG_PATH_SHORT);
72 if let Some(argument_value) = argument_value {
73 if argument_value.len() > 1 && argument_value.chars().next().unwrap() != '=' {
74 return None;
75 }
76 return Some(argument_value[1..].to_owned());
77 }
78 None
79}
80
81fn get_argument_value<'a>(
82 arguments: &'a Vec<String>,
83 short_arg: &str,
84 long_arg: &str,
85) -> Option<&'a str> {
86 for argument in arguments.iter() {
87 let index_option = argument.find(short_arg);
88 if let Some(index) = index_option {
89 return Some(&argument[index + short_arg.len()..]);
90 }
91
92 let index_option = argument.find(long_arg);
93 if let Some(index) = index_option {
94 return Some(&argument[index + long_arg.len()..]);
95 }
96 }
97 None
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use test_case::test_case;
104
105 #[test_case(ARG_INFO_LONG)]
106 #[test_case(ARG_INFO_SHORT)]
107 fn parses_is_need_to_show_help(arg: &str) {
108 let arguments = vec![arg.to_owned()];
109
110 let actual = parse_arguments(&arguments);
111
112 assert_eq!(true, actual.is_need_to_show_help);
113 }
114
115 #[test]
116 fn is_need_to_show_help_is_turned_off_by_default() {
117 let arguments = vec![];
118
119 let actual = parse_arguments(&arguments);
120
121 assert_eq!(false, actual.is_need_to_show_help);
122 }
123
124 #[test_case(ARG_VERSION_LONG)]
125 #[test_case(ARG_VERSION_SHORT)]
126 fn parses_is_need_to_show_version(arg: &str) {
127 let arguments = vec![arg.to_owned()];
128
129 let actual = parse_arguments(&arguments);
130
131 assert_eq!(true, actual.is_need_to_show_version);
132 }
133
134 #[test]
135 fn is_need_to_show_version_is_turned_off_by_default() {
136 let arguments = vec![];
137
138 let actual = parse_arguments(&arguments);
139
140 assert_eq!(false, actual.is_need_to_show_version);
141 }
142
143 #[test_case(ARG_NO_PROGRESS_LONG)]
144 #[test_case(ARG_NO_PROGRESS_SHORT)]
145 fn parses_is_need_to_show_progress(arg: &str) {
146 let arguments = vec![arg.to_owned()];
147
148 let actual = parse_arguments(&arguments);
149
150 assert_eq!(false, actual.is_need_to_show_progress);
151 }
152
153 #[test]
154 fn is_need_to_show_progress_is_turned_on_by_default() {
155 let arguments = vec![];
156
157 let actual = parse_arguments(&arguments);
158
159 assert_eq!(true, actual.is_need_to_show_progress);
160 }
161
162 #[test_case(ARG_OUTPUT_LONG)]
163 #[test_case(ARG_OUTPUT_SHORT)]
164 fn parses_json_output_type(arg: &str) {
165 let arguments = vec![format!("{}{}", arg, "=json")];
166
167 let actual = parse_arguments(&arguments);
168
169 assert_eq!(OutputType::Json, actual.output_type.unwrap());
170 }
171
172 #[test_case(ARG_OUTPUT_LONG)]
173 #[test_case(ARG_OUTPUT_SHORT)]
174 fn parses_plain_output_type(arg: &str) {
175 let arguments = vec![format!("{}{}", arg, "=plain")];
176
177 let actual = parse_arguments(&arguments);
178
179 assert_eq!(OutputType::Plain, actual.output_type.unwrap());
180 }
181
182 const INVALID_PARAM1: &str = "=xml";
183 const INVALID_PARAM2: &str = "=";
184 const INVALID_PARAM3: &str = "";
185
186 #[test_case(INVALID_PARAM1)]
187 #[test_case(INVALID_PARAM2)]
188 #[test_case(INVALID_PARAM3)]
189 fn parses_none_long_output_type(arg_value: &str) {
190 let arguments = vec![format!("{}{}", ARG_OUTPUT_LONG, arg_value)];
191
192 let actual = parse_arguments(&arguments);
193
194 assert_eq!(None, actual.output_type);
195 }
196
197 #[test_case(INVALID_PARAM1)]
198 #[test_case(INVALID_PARAM2)]
199 #[test_case(INVALID_PARAM3)]
200 fn parses_none_short_output_type(arg_value: &str) {
201 let arguments = vec![format!("{}{}", ARG_OUTPUT_SHORT, arg_value)];
202
203 let actual = parse_arguments(&arguments);
204
205 assert_eq!(None, actual.output_type);
206 }
207
208 #[test]
209 fn output_type_is_plain_by_default() {
210 let arguments = vec![];
211
212 let actual = parse_arguments(&arguments);
213
214 assert_eq!(OutputType::Plain, actual.output_type.unwrap());
215 }
216
217 #[test_case(ARG_PATH_LONG)]
218 #[test_case(ARG_PATH_SHORT)]
219 fn parses_path(arg: &str) {
220 let arguments = vec![format!("{}{}", arg, "=/a/b/c")];
221
222 let actual = parse_arguments(&arguments);
223
224 assert_eq!("/a/b/c", &actual.path.unwrap());
225 }
226
227 #[test_case(ARG_PATH_LONG)]
228 #[test_case(ARG_PATH_SHORT)]
229 fn parses_incorrect_path(arg: &str) {
230 let arguments = vec![format!("{}{}", arg, "/a/b/c")];
231
232 let actual = parse_arguments(&arguments);
233
234 assert_eq!(None, actual.path);
235 }
236}