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
78impl Args {
79 pub fn with_template_names(mut self, template_names: Vec<String>) -> Self {
92 self.template_names = template_names;
93 self
94 }
95
96 pub fn with_server_url(mut self, server_url: &str) -> Self {
109 self.server_url = server_url.to_string();
110 self
111 }
112
113 pub fn with_generator_uri(mut self, generator_uri: &str) -> Self {
126 self.generator_uri = generator_uri.to_string();
127 self
128 }
129
130 pub fn with_lister_uri(mut self, lister_uri: &str) -> Self {
142 self.lister_uri = lister_uri.to_string();
143 self
144 }
145
146 pub fn with_show_list(mut self, show_list: bool) -> Self {
159 self.show_list = show_list;
160 self
161 }
162}
163
164pub trait ArgsParser {
169 fn parse(&self, args: impl IntoIterator<Item = OsString>) -> Args;
184
185 fn try_parse(
205 &self,
206 args: impl IntoIterator<Item = OsString>,
207 ) -> Result<Args, ProgramExit>;
208}
209
210#[cfg(test)]
211mod tests {
212 use rstest::*;
213
214 use super::*;
215 use crate::helper::*;
216
217 mod default_args_parser {
218 use super::*;
219
220 mod try_parse {
221 use super::*;
222
223 mod success {
224 use super::*;
225 use crate::{ExitKind, constant};
226
227 #[rstest]
228 #[case("-V")]
229 #[case("--version")]
230 #[case("-V rust")]
231 #[case("rust -V")]
232 #[case("rust -s foo -V")]
233 #[case("rust -g bar -V")]
234 #[case("rust -i bar -V")]
235 #[case("-aV")]
236 #[case("rust -l -V")]
237 fn it_parses_version_cli_option(#[case] cli_args: &str) {
238 let cli_args = parse_cli_args(cli_args);
239 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
240
241 let actual_error = parsed_args.as_ref().err();
242 let expected_error = ProgramExit {
243 message: format!(
244 "{} {}",
245 env!("CARGO_PKG_NAME"),
246 env!("CARGO_PKG_VERSION")
247 ),
248 exit_status: 0,
249 styled_message: None,
250 kind: ExitKind::VersionInfos,
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("-h")]
260 #[case("--help")]
261 #[case("-h rust")]
262 #[case("rust -h")]
263 #[case("rust -s foo -h")]
264 #[case("rust -g bar -h")]
265 #[case("rust -i bar -h")]
266 #[case("-aVh")]
267 #[case("rust -l -h")]
268 fn it_parses_help_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: get_help_message(),
275 exit_status: 0,
276 styled_message: Some(get_ansi_help_message()),
277 kind: ExitKind::HelpInfos,
278 };
279 let expected_error = Some(&expected_error);
280
281 assert!(actual_error.is_some());
282 assert_eq!(actual_error, expected_error);
283 }
284
285 #[rstest]
286 #[case("-a")]
287 #[case("--author")]
288 #[case("-a rust")]
289 #[case("rust -a")]
290 #[case("rust -s foo -a")]
291 #[case("rust -g bar -a")]
292 #[case("rust -i bar -a")]
293 #[case("rust -l -a")]
294 fn it_parses_author_cli_option_preemptively(
295 #[case] cli_args: &str,
296 ) {
297 let cli_args = parse_cli_args(cli_args);
298 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
299
300 let actual_error = parsed_args.as_ref().err();
301 let expected_error = ProgramExit {
302 message: env!("CARGO_PKG_AUTHORS").to_string(),
303 exit_status: 0,
304 styled_message: None,
305 kind: ExitKind::AuthorInfos,
306 };
307 let expected_error = Some(&expected_error);
308
309 assert!(actual_error.is_some());
310 assert_eq!(actual_error, expected_error);
311 }
312
313 #[rstest]
314 #[case("rust")]
315 #[case("rust python node")]
316 fn it_parses_pos_args_without_server_url_cli_option(
317 #[case] cli_options: &str,
318 ) {
319 let cli_args = parse_cli_args(cli_options);
320 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
321
322 let actual_result = parsed_args.as_ref().ok();
323 let expected_result = Args::default()
324 .with_template_names(make_string_vec(cli_options))
325 .with_server_url(constant::template_manager::BASE_URL)
326 .with_generator_uri(
327 constant::template_manager::GENERATOR_URI,
328 )
329 .with_lister_uri(
330 constant::template_manager::LISTER_URI,
331 );
332 let expected_result = Some(&expected_result);
333
334 println!("{:?}", parsed_args);
335 assert!(actual_result.is_some());
336 assert_eq!(actual_result, expected_result);
337 }
338
339 #[rstest]
340 #[case("rust -s https://test.com")]
341 #[case("rust --server-url https://test.com")]
342 fn it_parses_pos_args_with_server_url_cli_option(
343 #[case] cli_args: &str,
344 ) {
345 let cli_args = parse_cli_args(cli_args);
346 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
347
348 let actual_result = parsed_args.as_ref().ok();
349 let expected_result = Args::default()
350 .with_template_names(make_string_vec("rust"))
351 .with_server_url("https://test.com")
352 .with_generator_uri(
353 constant::template_manager::GENERATOR_URI,
354 )
355 .with_lister_uri(
356 constant::template_manager::LISTER_URI,
357 );
358 let expected_result = Some(&expected_result);
359
360 assert!(actual_result.is_some());
361 assert_eq!(actual_result, expected_result);
362 }
363
364 #[rstest]
365 #[case("rust -g /test/api")]
366 #[case("rust --generator-uri /test/api")]
367 fn it_parses_pos_args_with_generator_uri_cli_option(
368 #[case] cli_args: &str,
369 ) {
370 let cli_args = parse_cli_args(cli_args);
371 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
372
373 let actual_result = parsed_args.as_ref().ok();
374 let expected_result = Args::default()
375 .with_template_names(make_string_vec("rust"))
376 .with_server_url(constant::template_manager::BASE_URL)
377 .with_generator_uri("/test/api")
378 .with_lister_uri(
379 constant::template_manager::LISTER_URI,
380 );
381 let expected_result = Some(&expected_result);
382
383 assert!(actual_result.is_some());
384 assert_eq!(actual_result, expected_result);
385 }
386
387 #[rstest]
388 #[case("rust -i /test/api")]
389 #[case("rust --lister-uri /test/api")]
390 fn it_parses_pos_args_with_lister_uri_cli_option(
391 #[case] cli_args: &str,
392 ) {
393 let cli_args = parse_cli_args(cli_args);
394 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
395
396 let actual_result = parsed_args.as_ref().ok();
397 let expected_result = Args::default()
398 .with_template_names(make_string_vec("rust"))
399 .with_server_url(constant::template_manager::BASE_URL)
400 .with_generator_uri(
401 constant::template_manager::GENERATOR_URI,
402 )
403 .with_lister_uri("/test/api");
404 let expected_result = Some(&expected_result);
405
406 assert!(actual_result.is_some());
407 assert_eq!(actual_result, expected_result);
408 }
409
410 #[rstest]
411 #[case("-l", "")]
412 #[case("--list", "")]
413 #[case("rust --list", "rust")]
414 #[case("rust python --list", "rust python")]
415 fn it_parses_list_cli_option(
416 #[case] cli_args: &str,
417 #[case] template_names: &str,
418 ) {
419 let cli_args = parse_cli_args(cli_args);
420 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
421
422 let actual_result = parsed_args.as_ref().ok();
423 let expected_result = Args::default()
424 .with_template_names(make_string_vec(template_names))
425 .with_server_url(constant::template_manager::BASE_URL)
426 .with_generator_uri(
427 constant::template_manager::GENERATOR_URI,
428 )
429 .with_show_list(true)
430 .with_lister_uri(
431 constant::template_manager::LISTER_URI,
432 );
433 let expected_result = Some(&expected_result);
434
435 assert!(actual_result.is_some());
436 assert_eq!(actual_result, expected_result);
437 }
438 }
439
440 mod failure {
441 use super::*;
442 use crate::{ExitKind, constant};
443
444 #[test]
445 fn it_fails_parsing_when_no_pos_args_given() {
446 let cli_args = parse_cli_args("");
447 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
448
449 let actual_error = parsed_args.as_ref().err();
450 let expected_error = ProgramExit {
451 message: load_expectation_file_as_string(
452 "no_pos_args_error",
453 ),
454 exit_status: constant::exit_status::GENERIC,
455
456 styled_message: Some(load_expectation_file_as_string(
457 "ansi_no_pos_args_error",
458 )),
459 kind: ExitKind::Error,
460 };
461 let expected_error = Some(&expected_error);
462
463 assert!(actual_error.is_some());
464 assert_eq!(actual_error, expected_error);
465 }
466
467 #[test]
468 fn it_fails_parsing_when_commas_in_pos_args() {
469 let cli_args = parse_cli_args("python,java");
470 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
471
472 let actual_error = parsed_args.as_ref().err();
473 let expected_error = ProgramExit {
474 message: load_expectation_file_as_string(
475 "comma_pos_args_error",
476 ),
477 exit_status: constant::exit_status::GENERIC,
478
479 styled_message: Some(load_expectation_file_as_string(
480 "ansi_comma_pos_args_error",
481 )),
482 kind: ExitKind::Error,
483 };
484 let expected_error = Some(&expected_error);
485
486 assert!(actual_error.is_some());
487 assert_eq!(actual_error, expected_error);
488 }
489
490 #[test]
491 fn it_fails_parsing_when_whitespaces_in_pos_args() {
492 let cli_args = vec![
493 OsString::from(env!("CARGO_PKG_NAME")),
494 OsString::from("r "),
495 ];
496 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
497
498 let actual_error = parsed_args.as_ref().err();
499 let expected_error = ProgramExit {
500 message: load_expectation_file_as_string(
501 "whitespace_pos_args_error",
502 ),
503 exit_status: constant::exit_status::GENERIC,
504
505 styled_message: Some(load_expectation_file_as_string(
506 "ansi_whitespace_pos_args_error",
507 )),
508 kind: ExitKind::Error,
509 };
510 let expected_error = Some(&expected_error);
511
512 assert!(actual_error.is_some());
513 assert_eq!(actual_error, expected_error);
514 }
515
516 #[test]
517 fn it_fails_parsing_when_commas_and_whitespaces_in_pos_args() {
518 let cli_args = vec![
519 OsString::from(env!("CARGO_PKG_NAME")),
520 OsString::from("r ,"),
521 ];
522 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
523
524 let actual_error = parsed_args.as_ref().err();
525 let expected_error = ProgramExit {
526 message: load_expectation_file_as_string(
527 "comma_whitespace_pos_args_error",
528 ),
529 exit_status: constant::exit_status::GENERIC,
530
531 styled_message: Some(load_expectation_file_as_string(
532 "ansi_comma_whitespace_pos_args_error",
533 )),
534 kind: ExitKind::Error,
535 };
536 let expected_error = Some(&expected_error);
537
538 assert!(actual_error.is_some());
539 assert_eq!(actual_error, expected_error);
540 }
541
542 #[test]
543 fn it_fails_parsing_when_server_url_but_no_pos_args() {
544 let cli_args = parse_cli_args("-s https://test.com");
545 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
546
547 let actual_error = parsed_args.as_ref().err();
548 let expected_error = ProgramExit {
549 message: load_expectation_file_as_string(
550 "server_url_no_pos_args_error",
551 ),
552 exit_status: constant::exit_status::GENERIC,
553
554 styled_message: Some(load_expectation_file_as_string(
555 "ansi_server_url_no_pos_args_error",
556 )),
557 kind: ExitKind::Error,
558 };
559 let expected_error = Some(&expected_error);
560
561 assert!(actual_error.is_some());
562 assert_eq!(actual_error, expected_error);
563 }
564
565 #[test]
566 fn it_fails_parsing_when_generator_uri_but_no_pos_args() {
567 let cli_args = parse_cli_args("-g /test/api");
568 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
569
570 let actual_error = parsed_args.as_ref().err();
571 let expected_error = ProgramExit {
572 message: load_expectation_file_as_string(
573 "generator_uri_no_pos_args_error",
574 ),
575 exit_status: constant::exit_status::GENERIC,
576
577 styled_message: Some(load_expectation_file_as_string(
578 "ansi_generator_uri_no_pos_args_error",
579 )),
580 kind: ExitKind::Error,
581 };
582 let expected_error = Some(&expected_error);
583
584 assert!(actual_error.is_some());
585 assert_eq!(actual_error, expected_error);
586 }
587
588 #[test]
589 fn it_fails_parsing_when_lister_uri_but_no_pos_args() {
590 let cli_args = parse_cli_args("-i /test/api");
591 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
592
593 let actual_error = parsed_args.as_ref().err();
594 let expected_error = ProgramExit {
595 message: load_expectation_file_as_string(
596 "lister_uri_no_pos_args_error",
597 ),
598 exit_status: constant::exit_status::GENERIC,
599
600 styled_message: Some(load_expectation_file_as_string(
601 "ansi_lister_uri_no_pos_args_error",
602 )),
603 kind: ExitKind::Error,
604 };
605 let expected_error = Some(&expected_error);
606
607 assert!(actual_error.is_some());
608 assert_eq!(actual_error, expected_error);
609 }
610
611 #[test]
612 fn it_fails_parsing_when_inexistent_cli_option() {
613 let cli_args = parse_cli_args("-x");
614 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
615
616 let actual_error = parsed_args.as_ref().err();
617 let expected_error = ProgramExit {
618 message: load_expectation_file_as_string(
619 "unexpected_argument_error",
620 ),
621 exit_status: constant::exit_status::GENERIC,
622 styled_message: Some(load_expectation_file_as_string(
623 "ansi_unexpected_argument_error",
624 )),
625 kind: ExitKind::Error,
626 };
627 let expected_error = Some(&expected_error);
628
629 assert!(actual_error.is_some());
630 assert_eq!(actual_error, expected_error);
631 }
632 }
633 }
634
635 mod parse {
636 use super::*;
637
638 mod success {
639 use super::*;
640
641 #[test]
642 fn it_parses_given_cli_options() {
643 let cli_args =
644 parse_cli_args("rust python -s test -g foo -i bar");
645
646 let actual_result = ClapArgsParser::new().parse(cli_args);
647 let expected_result = Args::default()
648 .with_template_names(make_string_vec("rust python"))
649 .with_server_url("test")
650 .with_generator_uri("foo")
651 .with_lister_uri("bar");
652
653 assert_eq!(actual_result, expected_result);
654 }
655 }
656 }
657 }
658}