gitignore_template_generator/parser/
api.rs1use std::ffi::OsString;
2
3use crate::ProgramExit;
4pub use crate::parser::impls::ClapArgsParser;
5
6#[derive(Debug, PartialEq, Default)]
11pub struct Args {
12 pub template_names: Vec<String>,
18
19 pub server_url: String,
27
28 pub endpoint_uri: String,
36
37 pub show_help: bool,
44
45 pub show_version: bool,
52
53 pub show_author: bool,
59}
60
61impl Args {
62 pub fn with_template_names(mut self, template_names: Vec<String>) -> Self {
75 self.template_names = template_names;
76 self
77 }
78
79 pub fn with_server_url(mut self, server_url: &str) -> Self {
92 self.server_url = server_url.to_string();
93 self
94 }
95
96 pub fn with_endpoint_uri(mut self, endpoint_uri: &str) -> Self {
109 self.endpoint_uri = endpoint_uri.to_string();
110 self
111 }
112}
113
114pub trait ArgsParser {
119 fn parse(&self, args: impl IntoIterator<Item = OsString>) -> Args;
134
135 fn try_parse(
155 &self,
156 args: impl IntoIterator<Item = OsString>,
157 ) -> Result<Args, ProgramExit>;
158}
159
160#[cfg(test)]
161mod tests {
162 use rstest::*;
163
164 use super::*;
165 use crate::helper::*;
166
167 mod default_args_parser {
168 use super::*;
169
170 mod try_parse {
171 use super::*;
172
173 mod success {
174 use crate::{ExitKind, constant};
175
176 use super::*;
177
178 #[rstest]
179 #[case("-V")]
180 #[case("--version")]
181 #[case("-V rust")]
182 #[case("rust -V")]
183 #[case("rust -s foo -V")]
184 #[case("rust -e bar -V")]
185 #[case("-aV")]
186 fn it_parses_version_cli_option(#[case] cli_args: &str) {
187 let cli_args = parse_cli_args(cli_args);
188 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
189
190 let actual_error = parsed_args.as_ref().err();
191 let expected_error = ProgramExit {
192 message: format!(
193 "{} {}",
194 env!("CARGO_PKG_NAME"),
195 env!("CARGO_PKG_VERSION")
196 ),
197 exit_status: 0,
198 styled_message: None,
199 kind: ExitKind::VersionInfos,
200 };
201 let expected_error = Some(&expected_error);
202
203 assert!(actual_error.is_some());
204 assert_eq!(actual_error, expected_error);
205 }
206
207 #[rstest]
208 #[case("-h")]
209 #[case("--help")]
210 #[case("-h rust")]
211 #[case("rust -h")]
212 #[case("rust -s foo -h")]
213 #[case("rust -e bar -h")]
214 #[case("-aVh")]
215 fn it_parses_help_cli_option(#[case] cli_args: &str) {
216 let cli_args = parse_cli_args(cli_args);
217 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
218
219 let actual_error = parsed_args.as_ref().err();
220 let expected_error = ProgramExit {
221 message: get_help_message(),
222 exit_status: 0,
223 styled_message: Some(get_ansi_help_message()),
224 kind: ExitKind::HelpInfos,
225 };
226 let expected_error = Some(&expected_error);
227
228 assert!(actual_error.is_some());
229 assert_eq!(actual_error, expected_error);
230 }
231
232 #[rstest]
233 #[case("-a")]
234 #[case("--author")]
235 #[case("-a rust")]
236 #[case("rust -a")]
237 #[case("rust -s foo -a")]
238 #[case("rust -e bar -a")]
239 fn it_parses_author_cli_option_preemptively(
240 #[case] cli_args: &str,
241 ) {
242 let cli_args = parse_cli_args(cli_args);
243 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
244
245 let actual_error = parsed_args.as_ref().err();
246 let expected_error = ProgramExit {
247 message: env!("CARGO_PKG_AUTHORS").to_string(),
248 exit_status: 0,
249 styled_message: None,
250 kind: ExitKind::AuthorInfos,
251 };
252 let expected_error = Some(&expected_error);
253
254 assert!(actual_error.is_some());
255 assert_eq!(actual_error, expected_error);
256 }
257
258 #[rstest]
259 #[case("rust")]
260 #[case("rust python node")]
261 fn it_parses_pos_args_without_server_url_cli_option(
262 #[case] cli_options: &str,
263 ) {
264 let cli_args = parse_cli_args(cli_options);
265 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
266
267 let actual_result = parsed_args.as_ref().ok();
268 let expected_result = Args::default()
269 .with_template_names(make_string_vec(cli_options))
270 .with_server_url(constant::template_generator::BASE_URL)
271 .with_endpoint_uri(constant::template_generator::URI);
272 let expected_result = Some(&expected_result);
273
274 println!("{:?}", parsed_args);
275 assert!(actual_result.is_some());
276 assert_eq!(actual_result, expected_result);
277 }
278
279 #[rstest]
280 #[case("rust -s https://test.com")]
281 #[case("rust --server-url https://test.com")]
282 fn it_parses_pos_args_with_server_url_cli_option(
283 #[case] cli_args: &str,
284 ) {
285 let cli_args = parse_cli_args(cli_args);
286 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
287
288 let actual_result = parsed_args.as_ref().ok();
289 let expected_result = Args::default()
290 .with_template_names(make_string_vec("rust"))
291 .with_server_url("https://test.com")
292 .with_endpoint_uri(constant::template_generator::URI);
293 let expected_result = Some(&expected_result);
294
295 assert!(actual_result.is_some());
296 assert_eq!(actual_result, expected_result);
297 }
298
299 #[rstest]
300 #[case("rust -e /test/api")]
301 #[case("rust --endpoint-uri /test/api")]
302 fn it_parses_pos_args_with_endpoint_uri_cli_option(
303 #[case] cli_args: &str,
304 ) {
305 let cli_args = parse_cli_args(cli_args);
306 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
307
308 let actual_result = parsed_args.as_ref().ok();
309 let expected_result = Args::default()
310 .with_template_names(make_string_vec("rust"))
311 .with_server_url(constant::template_generator::BASE_URL)
312 .with_endpoint_uri("/test/api");
313 let expected_result = Some(&expected_result);
314
315 assert!(actual_result.is_some());
316 assert_eq!(actual_result, expected_result);
317 }
318 }
319
320 mod failure {
321 use crate::{ExitKind, constant};
322
323 use super::*;
324
325 #[test]
326 fn it_fails_parsing_when_no_pos_args_given() {
327 let cli_args = parse_cli_args("");
328 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
329
330 let actual_error = parsed_args.as_ref().err();
331 let expected_error = ProgramExit {
332 message: load_expectation_file_as_string(
333 "no_pos_args_error",
334 ),
335 exit_status: constant::exit_status::GENERIC,
336
337 styled_message: Some(load_expectation_file_as_string(
338 "ansi_no_pos_args_error",
339 )),
340 kind: ExitKind::Error,
341 };
342 let expected_error = Some(&expected_error);
343
344 assert!(actual_error.is_some());
345 assert_eq!(actual_error, expected_error);
346 }
347
348 #[test]
349 fn it_fails_parsing_when_commas_in_pos_args() {
350 let cli_args = parse_cli_args("python,java");
351 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
352
353 let actual_error = parsed_args.as_ref().err();
354 let expected_error = ProgramExit {
355 message: load_expectation_file_as_string(
356 "comma_pos_args_error",
357 ),
358 exit_status: constant::exit_status::GENERIC,
359
360 styled_message: Some(load_expectation_file_as_string(
361 "ansi_comma_pos_args_error",
362 )),
363 kind: ExitKind::Error,
364 };
365 let expected_error = Some(&expected_error);
366
367 assert!(actual_error.is_some());
368 assert_eq!(actual_error, expected_error);
369 }
370
371 #[test]
372 fn it_fails_parsing_when_whitespaces_in_pos_args() {
373 let cli_args = vec![
374 OsString::from(env!("CARGO_PKG_NAME")),
375 OsString::from("r "),
376 ];
377 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
378
379 let actual_error = parsed_args.as_ref().err();
380 let expected_error = ProgramExit {
381 message: load_expectation_file_as_string(
382 "whitespace_pos_args_error",
383 ),
384 exit_status: constant::exit_status::GENERIC,
385
386 styled_message: Some(load_expectation_file_as_string(
387 "ansi_whitespace_pos_args_error",
388 )),
389 kind: ExitKind::Error,
390 };
391 let expected_error = Some(&expected_error);
392
393 assert!(actual_error.is_some());
394 assert_eq!(actual_error, expected_error);
395 }
396
397 #[test]
398 fn it_fails_parsing_when_commas_and_whitespaces_in_pos_args() {
399 let cli_args = vec![
400 OsString::from(env!("CARGO_PKG_NAME")),
401 OsString::from("r ,"),
402 ];
403 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
404
405 let actual_error = parsed_args.as_ref().err();
406 let expected_error = ProgramExit {
407 message: load_expectation_file_as_string(
408 "comma_whitespace_pos_args_error",
409 ),
410 exit_status: constant::exit_status::GENERIC,
411
412 styled_message: Some(load_expectation_file_as_string(
413 "ansi_comma_whitespace_pos_args_error",
414 )),
415 kind: ExitKind::Error,
416 };
417 let expected_error = Some(&expected_error);
418
419 assert!(actual_error.is_some());
420 assert_eq!(actual_error, expected_error);
421 }
422
423 #[test]
424 fn it_fails_parsing_when_server_url_but_no_pos_args() {
425 let cli_args = parse_cli_args("-s https://test.com");
426 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
427
428 let actual_error = parsed_args.as_ref().err();
429 let expected_error = ProgramExit {
430 message: load_expectation_file_as_string(
431 "server_url_no_pos_args_error",
432 ),
433 exit_status: constant::exit_status::GENERIC,
434
435 styled_message: Some(load_expectation_file_as_string(
436 "ansi_server_url_no_pos_args_error",
437 )),
438 kind: ExitKind::Error,
439 };
440 let expected_error = Some(&expected_error);
441
442 assert!(actual_error.is_some());
443 assert_eq!(actual_error, expected_error);
444 }
445
446 #[test]
447 fn it_fails_parsing_when_endpoint_uri_but_no_pos_args() {
448 let cli_args = parse_cli_args("-e /test/api");
449 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
450
451 let actual_error = parsed_args.as_ref().err();
452 let expected_error = ProgramExit {
453 message: load_expectation_file_as_string(
454 "endpoint_uri_no_pos_args_error",
455 ),
456 exit_status: constant::exit_status::GENERIC,
457
458 styled_message: Some(load_expectation_file_as_string(
459 "ansi_endpoint_uri_no_pos_args_error",
460 )),
461 kind: ExitKind::Error,
462 };
463 let expected_error = Some(&expected_error);
464
465 assert!(actual_error.is_some());
466 assert_eq!(actual_error, expected_error);
467 }
468
469 #[test]
470 fn it_fails_parsing_when_inexistent_cli_option() {
471 let cli_args = parse_cli_args("-x");
472 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
473
474 let actual_error = parsed_args.as_ref().err();
475 let expected_error = ProgramExit {
476 message: load_expectation_file_as_string(
477 "unexpected_argument_error",
478 ),
479 exit_status: constant::exit_status::GENERIC,
480 styled_message: Some(load_expectation_file_as_string(
481 "ansi_unexpected_argument_error",
482 )),
483 kind: ExitKind::Error,
484 };
485 let expected_error = Some(&expected_error);
486
487 assert!(actual_error.is_some());
488 assert_eq!(actual_error, expected_error);
489 }
490 }
491 }
492 }
493}