duplicate_finder/
args.rs

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}