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 generator_uri: String,
36
37 pub lister_uri: String,
45
46 pub show_help: bool,
53
54 pub show_version: bool,
61
62 pub show_author: bool,
68
69 pub show_list: bool,
76
77 pub check_template_names: bool,
86}
87
88impl Args {
89 pub fn with_template_names(mut self, template_names: Vec<String>) -> Self {
102 self.template_names = template_names;
103 self
104 }
105
106 pub fn with_server_url(mut self, server_url: &str) -> Self {
119 self.server_url = server_url.to_string();
120 self
121 }
122
123 pub fn with_generator_uri(mut self, generator_uri: &str) -> Self {
136 self.generator_uri = generator_uri.to_string();
137 self
138 }
139
140 pub fn with_lister_uri(mut self, lister_uri: &str) -> Self {
152 self.lister_uri = lister_uri.to_string();
153 self
154 }
155
156 pub fn with_show_list(mut self, show_list: bool) -> Self {
169 self.show_list = show_list;
170 self
171 }
172
173 pub fn with_check_template_names(
186 mut self,
187 check_template_names: bool,
188 ) -> Self {
189 self.check_template_names = check_template_names;
190 self
191 }
192}
193
194pub trait ArgsParser {
199 fn parse(&self, args: impl IntoIterator<Item = OsString>) -> Args;
214
215 fn try_parse(
235 &self,
236 args: impl IntoIterator<Item = OsString>,
237 ) -> Result<Args, ProgramExit>;
238}
239
240#[cfg(test)]
241mod tests {
242 use rstest::*;
243
244 use super::*;
245 use crate::helper::*;
246
247 mod default_args_parser {
248 use super::*;
249
250 mod try_parse {
251 use super::*;
252
253 mod success {
254 use super::*;
255 use crate::{ExitKind, constant};
256
257 #[rstest]
258 #[case("-V")]
259 #[case("--version")]
260 #[case("-V rust")]
261 #[case("rust -V")]
262 #[case("rust -s foo -V")]
263 #[case("rust -g bar -V")]
264 #[case("rust -i bar -V")]
265 #[case("rust -c bar -V")]
266 #[case("-aV")]
267 #[case("rust -l -V")]
268 fn it_parses_version_cli_option(#[case] cli_args: &str) {
269 let cli_args = parse_cli_args(cli_args);
270 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
271
272 let actual_error = parsed_args.as_ref().err();
273 let expected_error = ProgramExit {
274 message: format!(
275 "{} {}",
276 env!("CARGO_PKG_NAME"),
277 env!("CARGO_PKG_VERSION")
278 ),
279 exit_status: 0,
280 styled_message: None,
281 kind: ExitKind::VersionInfos,
282 };
283 let expected_error = Some(&expected_error);
284
285 assert!(actual_error.is_some());
286 assert_eq!(actual_error, expected_error);
287 }
288
289 #[rstest]
290 #[case("-h")]
291 #[case("--help")]
292 #[case("-h rust")]
293 #[case("rust -h")]
294 #[case("rust -s foo -h")]
295 #[case("rust -g bar -h")]
296 #[case("rust -i bar -h")]
297 #[case("rust -c bar -h")]
298 #[case("-aVh")]
299 #[case("rust -l -h")]
300 fn it_parses_help_cli_option(#[case] cli_args: &str) {
301 let cli_args = parse_cli_args(cli_args);
302 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
303
304 let actual_error = parsed_args.as_ref().err();
305 let expected_error = ProgramExit {
306 message: get_help_message(),
307 exit_status: 0,
308 styled_message: Some(get_ansi_help_message()),
309 kind: ExitKind::HelpInfos,
310 };
311 let expected_error = Some(&expected_error);
312
313 assert!(actual_error.is_some());
314 assert_eq!(actual_error, expected_error);
315 }
316
317 #[rstest]
318 #[case("-a")]
319 #[case("--author")]
320 #[case("-a rust")]
321 #[case("rust -a")]
322 #[case("rust -s foo -a")]
323 #[case("rust -g bar -a")]
324 #[case("rust -i bar -a")]
325 #[case("rust -c bar -a")]
326 #[case("rust -l -a")]
327 fn it_parses_author_cli_option_preemptively(
328 #[case] cli_args: &str,
329 ) {
330 let cli_args = parse_cli_args(cli_args);
331 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
332
333 let actual_error = parsed_args.as_ref().err();
334 let expected_error = ProgramExit {
335 message: env!("CARGO_PKG_AUTHORS").to_string(),
336 exit_status: 0,
337 styled_message: None,
338 kind: ExitKind::AuthorInfos,
339 };
340 let expected_error = Some(&expected_error);
341
342 assert!(actual_error.is_some());
343 assert_eq!(actual_error, expected_error);
344 }
345
346 #[rstest]
347 #[case("rust")]
348 #[case("rust python node")]
349 fn it_parses_pos_args_without_server_url_cli_option(
350 #[case] cli_options: &str,
351 ) {
352 let cli_args = parse_cli_args(cli_options);
353 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
354
355 let actual_result = parsed_args.as_ref().ok();
356 let expected_result = Args::default()
357 .with_template_names(make_string_vec(cli_options))
358 .with_server_url(constant::template_manager::BASE_URL)
359 .with_generator_uri(
360 constant::template_manager::GENERATOR_URI,
361 )
362 .with_lister_uri(
363 constant::template_manager::LISTER_URI,
364 );
365 let expected_result = Some(&expected_result);
366
367 println!("{:?}", parsed_args);
368 assert!(actual_result.is_some());
369 assert_eq!(actual_result, expected_result);
370 }
371
372 #[rstest]
373 #[case("rust -s https://test.com")]
374 #[case("rust --server-url https://test.com")]
375 fn it_parses_pos_args_with_server_url_cli_option(
376 #[case] cli_args: &str,
377 ) {
378 let cli_args = parse_cli_args(cli_args);
379 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
380
381 let actual_result = parsed_args.as_ref().ok();
382 let expected_result = Args::default()
383 .with_template_names(make_string_vec("rust"))
384 .with_server_url("https://test.com")
385 .with_generator_uri(
386 constant::template_manager::GENERATOR_URI,
387 )
388 .with_lister_uri(
389 constant::template_manager::LISTER_URI,
390 );
391 let expected_result = Some(&expected_result);
392
393 assert!(actual_result.is_some());
394 assert_eq!(actual_result, expected_result);
395 }
396
397 #[rstest]
398 #[case("rust -g /test/api")]
399 #[case("rust --generator-uri /test/api")]
400 fn it_parses_pos_args_with_generator_uri_cli_option(
401 #[case] cli_args: &str,
402 ) {
403 let cli_args = parse_cli_args(cli_args);
404 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
405
406 let actual_result = parsed_args.as_ref().ok();
407 let expected_result = Args::default()
408 .with_template_names(make_string_vec("rust"))
409 .with_server_url(constant::template_manager::BASE_URL)
410 .with_generator_uri("/test/api")
411 .with_lister_uri(
412 constant::template_manager::LISTER_URI,
413 );
414 let expected_result = Some(&expected_result);
415
416 assert!(actual_result.is_some());
417 assert_eq!(actual_result, expected_result);
418 }
419
420 #[rstest]
421 #[case("rust -i /test/api")]
422 #[case("rust --lister-uri /test/api")]
423 fn it_parses_pos_args_with_lister_uri_cli_option(
424 #[case] cli_args: &str,
425 ) {
426 let cli_args = parse_cli_args(cli_args);
427 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
428
429 let actual_result = parsed_args.as_ref().ok();
430 let expected_result = Args::default()
431 .with_template_names(make_string_vec("rust"))
432 .with_server_url(constant::template_manager::BASE_URL)
433 .with_generator_uri(
434 constant::template_manager::GENERATOR_URI,
435 )
436 .with_lister_uri("/test/api");
437 let expected_result = Some(&expected_result);
438
439 assert!(actual_result.is_some());
440 assert_eq!(actual_result, expected_result);
441 }
442
443 #[rstest]
444 #[case("-l", "")]
445 #[case("--list", "")]
446 #[case("rust --list", "rust")]
447 #[case("rust python --list", "rust python")]
448 fn it_parses_list_cli_option(
449 #[case] cli_args: &str,
450 #[case] template_names: &str,
451 ) {
452 let cli_args = parse_cli_args(cli_args);
453 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
454
455 let actual_result = parsed_args.as_ref().ok();
456 let expected_result = Args::default()
457 .with_template_names(make_string_vec(template_names))
458 .with_server_url(constant::template_manager::BASE_URL)
459 .with_generator_uri(
460 constant::template_manager::GENERATOR_URI,
461 )
462 .with_show_list(true)
463 .with_lister_uri(
464 constant::template_manager::LISTER_URI,
465 );
466 let expected_result = Some(&expected_result);
467
468 assert!(actual_result.is_some());
469 assert_eq!(actual_result, expected_result);
470 }
471
472 #[rstest]
473 #[case("rust python -c")]
474 #[case("rust python --check")]
475 fn it_parses_check_option(#[case] cli_args: &str) {
476 let cli_args = parse_cli_args(cli_args);
477 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
478
479 let actual_result = parsed_args.as_ref().ok();
480 let expected_result = Args::default()
481 .with_template_names(make_string_vec("rust python"))
482 .with_server_url(constant::template_manager::BASE_URL)
483 .with_generator_uri(
484 constant::template_manager::GENERATOR_URI,
485 )
486 .with_lister_uri(constant::template_manager::LISTER_URI)
487 .with_check_template_names(true);
488 let expected_result = Some(&expected_result);
489
490 assert!(actual_result.is_some());
491 assert_eq!(actual_result, expected_result);
492 }
493 }
494
495 mod failure {
496 use super::*;
497 use crate::{ExitKind, constant};
498
499 #[test]
500 fn it_fails_parsing_when_no_pos_args_given() {
501 let cli_args = parse_cli_args("");
502 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
503
504 let actual_error = parsed_args.as_ref().err();
505 let expected_error = ProgramExit {
506 message: load_expectation_file_as_string(
507 "no_pos_args_error",
508 ),
509 exit_status: constant::exit_status::GENERIC,
510
511 styled_message: Some(load_expectation_file_as_string(
512 "ansi_no_pos_args_error",
513 )),
514 kind: ExitKind::Error,
515 };
516 let expected_error = Some(&expected_error);
517
518 assert!(actual_error.is_some());
519 assert_eq!(actual_error, expected_error);
520 }
521
522 #[test]
523 fn it_fails_parsing_when_commas_in_pos_args() {
524 let cli_args = parse_cli_args("python,java");
525 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
526
527 let actual_error = parsed_args.as_ref().err();
528 let expected_error = ProgramExit {
529 message: load_expectation_file_as_string(
530 "comma_pos_args_error",
531 ),
532 exit_status: constant::exit_status::GENERIC,
533
534 styled_message: Some(load_expectation_file_as_string(
535 "ansi_comma_pos_args_error",
536 )),
537 kind: ExitKind::Error,
538 };
539 let expected_error = Some(&expected_error);
540
541 assert!(actual_error.is_some());
542 assert_eq!(actual_error, expected_error);
543 }
544
545 #[test]
546 fn it_fails_parsing_when_whitespaces_in_pos_args() {
547 let cli_args = vec![
548 OsString::from(env!("CARGO_PKG_NAME")),
549 OsString::from("r "),
550 ];
551 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
552
553 let actual_error = parsed_args.as_ref().err();
554 let expected_error = ProgramExit {
555 message: load_expectation_file_as_string(
556 "whitespace_pos_args_error",
557 ),
558 exit_status: constant::exit_status::GENERIC,
559
560 styled_message: Some(load_expectation_file_as_string(
561 "ansi_whitespace_pos_args_error",
562 )),
563 kind: ExitKind::Error,
564 };
565 let expected_error = Some(&expected_error);
566
567 assert!(actual_error.is_some());
568 assert_eq!(actual_error, expected_error);
569 }
570
571 #[test]
572 fn it_fails_parsing_when_commas_and_whitespaces_in_pos_args() {
573 let cli_args = vec![
574 OsString::from(env!("CARGO_PKG_NAME")),
575 OsString::from("r ,"),
576 ];
577 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
578
579 let actual_error = parsed_args.as_ref().err();
580 let expected_error = ProgramExit {
581 message: load_expectation_file_as_string(
582 "comma_whitespace_pos_args_error",
583 ),
584 exit_status: constant::exit_status::GENERIC,
585
586 styled_message: Some(load_expectation_file_as_string(
587 "ansi_comma_whitespace_pos_args_error",
588 )),
589 kind: ExitKind::Error,
590 };
591 let expected_error = Some(&expected_error);
592
593 assert!(actual_error.is_some());
594 assert_eq!(actual_error, expected_error);
595 }
596
597 #[test]
598 fn it_fails_parsing_when_server_url_but_no_pos_args() {
599 let cli_args = parse_cli_args("-s https://test.com");
600 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
601
602 let actual_error = parsed_args.as_ref().err();
603 let expected_error = ProgramExit {
604 message: load_expectation_file_as_string(
605 "server_url_no_pos_args_error",
606 ),
607 exit_status: constant::exit_status::GENERIC,
608
609 styled_message: Some(load_expectation_file_as_string(
610 "ansi_server_url_no_pos_args_error",
611 )),
612 kind: ExitKind::Error,
613 };
614 let expected_error = Some(&expected_error);
615
616 assert!(actual_error.is_some());
617 assert_eq!(actual_error, expected_error);
618 }
619
620 #[test]
621 fn it_fails_parsing_when_generator_uri_but_no_pos_args() {
622 let cli_args = parse_cli_args("-g /test/api");
623 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
624
625 let actual_error = parsed_args.as_ref().err();
626 let expected_error = ProgramExit {
627 message: load_expectation_file_as_string(
628 "generator_uri_no_pos_args_error",
629 ),
630 exit_status: constant::exit_status::GENERIC,
631
632 styled_message: Some(load_expectation_file_as_string(
633 "ansi_generator_uri_no_pos_args_error",
634 )),
635 kind: ExitKind::Error,
636 };
637 let expected_error = Some(&expected_error);
638
639 assert!(actual_error.is_some());
640 assert_eq!(actual_error, expected_error);
641 }
642
643 #[test]
644 fn it_fails_parsing_when_lister_uri_but_no_pos_args() {
645 let cli_args = parse_cli_args("-i /test/api");
646 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
647
648 let actual_error = parsed_args.as_ref().err();
649 let expected_error = ProgramExit {
650 message: load_expectation_file_as_string(
651 "lister_uri_no_pos_args_error",
652 ),
653 exit_status: constant::exit_status::GENERIC,
654
655 styled_message: Some(load_expectation_file_as_string(
656 "ansi_lister_uri_no_pos_args_error",
657 )),
658 kind: ExitKind::Error,
659 };
660 let expected_error = Some(&expected_error);
661
662 assert!(actual_error.is_some());
663 assert_eq!(actual_error, expected_error);
664 }
665
666 #[test]
667 fn it_fails_parsing_when_check_option_but_no_pos_args() {
668 let cli_args = parse_cli_args("--check");
669 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
670
671 let actual_error = parsed_args.as_ref().err();
672 let expected_error = ProgramExit {
673 message: load_expectation_file_as_string(
674 "check_option_no_pos_args_error",
675 ),
676 exit_status: constant::exit_status::GENERIC,
677
678 styled_message: Some(load_expectation_file_as_string(
679 "ansi_check_option_no_pos_args_error",
680 )),
681 kind: ExitKind::Error,
682 };
683 let expected_error = Some(&expected_error);
684
685 assert!(actual_error.is_some());
686 assert_eq!(actual_error, expected_error);
687 }
688
689 #[test]
690 fn it_fails_parsing_when_inexistent_cli_option() {
691 let cli_args = parse_cli_args("-x");
692 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
693
694 let actual_error = parsed_args.as_ref().err();
695 let expected_error = ProgramExit {
696 message: load_expectation_file_as_string(
697 "unexpected_argument_error",
698 ),
699 exit_status: constant::exit_status::GENERIC,
700 styled_message: Some(load_expectation_file_as_string(
701 "ansi_unexpected_argument_error",
702 )),
703 kind: ExitKind::Error,
704 };
705 let expected_error = Some(&expected_error);
706
707 assert!(actual_error.is_some());
708 assert_eq!(actual_error, expected_error);
709 }
710 }
711 }
712
713 mod parse {
714 use super::*;
715
716 mod success {
717 use super::*;
718
719 #[test]
720 fn it_parses_given_cli_options() {
721 let cli_args = parse_cli_args(
722 "rust python -s test -g foo -i bar --check --list",
723 );
724
725 let actual_result = ClapArgsParser::new().parse(cli_args);
726 let expected_result = Args::default()
727 .with_template_names(make_string_vec("rust python"))
728 .with_server_url("test")
729 .with_generator_uri("foo")
730 .with_lister_uri("bar")
731 .with_check_template_names(true)
732 .with_show_list(true);
733
734 assert_eq!(actual_result, expected_result);
735 }
736 }
737 }
738 }
739}