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 pub show_list: bool,
67}
68
69impl Args {
70 pub fn with_template_names(mut self, template_names: Vec<String>) -> Self {
83 self.template_names = template_names;
84 self
85 }
86
87 pub fn with_server_url(mut self, server_url: &str) -> Self {
100 self.server_url = server_url.to_string();
101 self
102 }
103
104 pub fn with_endpoint_uri(mut self, endpoint_uri: &str) -> Self {
117 self.endpoint_uri = endpoint_uri.to_string();
118 self
119 }
120
121 pub fn with_show_list(mut self, show_list: bool) -> Self {
134 self.show_list = show_list;
135 self
136 }
137}
138
139pub trait ArgsParser {
144 fn parse(&self, args: impl IntoIterator<Item = OsString>) -> Args;
159
160 fn try_parse(
180 &self,
181 args: impl IntoIterator<Item = OsString>,
182 ) -> Result<Args, ProgramExit>;
183}
184
185#[cfg(test)]
186mod tests {
187 use rstest::*;
188
189 use super::*;
190 use crate::helper::*;
191
192 mod default_args_parser {
193 use super::*;
194
195 mod try_parse {
196 use super::*;
197
198 mod success {
199 use super::*;
200 use crate::{ExitKind, constant};
201
202 #[rstest]
203 #[case("-V")]
204 #[case("--version")]
205 #[case("-V rust")]
206 #[case("rust -V")]
207 #[case("rust -s foo -V")]
208 #[case("rust -e bar -V")]
209 #[case("-aV")]
210 #[case("rust -l -V")]
211 fn it_parses_version_cli_option(#[case] cli_args: &str) {
212 let cli_args = parse_cli_args(cli_args);
213 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
214
215 let actual_error = parsed_args.as_ref().err();
216 let expected_error = ProgramExit {
217 message: format!(
218 "{} {}",
219 env!("CARGO_PKG_NAME"),
220 env!("CARGO_PKG_VERSION")
221 ),
222 exit_status: 0,
223 styled_message: None,
224 kind: ExitKind::VersionInfos,
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("-h")]
234 #[case("--help")]
235 #[case("-h rust")]
236 #[case("rust -h")]
237 #[case("rust -s foo -h")]
238 #[case("rust -e bar -h")]
239 #[case("-aVh")]
240 #[case("rust -l -h")]
241 fn it_parses_help_cli_option(#[case] cli_args: &str) {
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: get_help_message(),
248 exit_status: 0,
249 styled_message: Some(get_ansi_help_message()),
250 kind: ExitKind::HelpInfos,
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("-a")]
260 #[case("--author")]
261 #[case("-a rust")]
262 #[case("rust -a")]
263 #[case("rust -s foo -a")]
264 #[case("rust -e bar -a")]
265 #[case("rust -l -a")]
266 fn it_parses_author_cli_option_preemptively(
267 #[case] cli_args: &str,
268 ) {
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: env!("CARGO_PKG_AUTHORS").to_string(),
275 exit_status: 0,
276 styled_message: None,
277 kind: ExitKind::AuthorInfos,
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("rust")]
287 #[case("rust python node")]
288 fn it_parses_pos_args_without_server_url_cli_option(
289 #[case] cli_options: &str,
290 ) {
291 let cli_args = parse_cli_args(cli_options);
292 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
293
294 let actual_result = parsed_args.as_ref().ok();
295 let expected_result = Args::default()
296 .with_template_names(make_string_vec(cli_options))
297 .with_server_url(constant::template_manager::BASE_URL)
298 .with_endpoint_uri(
299 constant::template_manager::GENERATOR_URI,
300 );
301 let expected_result = Some(&expected_result);
302
303 println!("{:?}", parsed_args);
304 assert!(actual_result.is_some());
305 assert_eq!(actual_result, expected_result);
306 }
307
308 #[rstest]
309 #[case("rust -s https://test.com")]
310 #[case("rust --server-url https://test.com")]
311 fn it_parses_pos_args_with_server_url_cli_option(
312 #[case] cli_args: &str,
313 ) {
314 let cli_args = parse_cli_args(cli_args);
315 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
316
317 let actual_result = parsed_args.as_ref().ok();
318 let expected_result = Args::default()
319 .with_template_names(make_string_vec("rust"))
320 .with_server_url("https://test.com")
321 .with_endpoint_uri(
322 constant::template_manager::GENERATOR_URI,
323 );
324 let expected_result = Some(&expected_result);
325
326 assert!(actual_result.is_some());
327 assert_eq!(actual_result, expected_result);
328 }
329
330 #[rstest]
331 #[case("rust -e /test/api")]
332 #[case("rust --endpoint-uri /test/api")]
333 fn it_parses_pos_args_with_endpoint_uri_cli_option(
334 #[case] cli_args: &str,
335 ) {
336 let cli_args = parse_cli_args(cli_args);
337 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
338
339 let actual_result = parsed_args.as_ref().ok();
340 let expected_result = Args::default()
341 .with_template_names(make_string_vec("rust"))
342 .with_server_url(constant::template_manager::BASE_URL)
343 .with_endpoint_uri("/test/api");
344 let expected_result = Some(&expected_result);
345
346 assert!(actual_result.is_some());
347 assert_eq!(actual_result, expected_result);
348 }
349
350 #[rstest]
351 #[case("-l", "")]
352 #[case("--list", "")]
353 #[case("rust --list", "rust")]
354 #[case("rust python --list", "rust python")]
355 fn it_parses_list_cli_option(
356 #[case] cli_args: &str,
357 #[case] template_names: &str,
358 ) {
359 let cli_args = parse_cli_args(cli_args);
360 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
361
362 let actual_result = parsed_args.as_ref().ok();
363 let expected_result = Args::default()
364 .with_template_names(make_string_vec(template_names))
365 .with_server_url(constant::template_manager::BASE_URL)
366 .with_endpoint_uri(
367 constant::template_manager::GENERATOR_URI,
368 )
369 .with_show_list(true);
370 let expected_result = Some(&expected_result);
371
372 assert!(actual_result.is_some());
373 assert_eq!(actual_result, expected_result);
374 }
375 }
376
377 mod failure {
378 use super::*;
379 use crate::{ExitKind, constant};
380
381 #[test]
382 fn it_fails_parsing_when_no_pos_args_given() {
383 let cli_args = parse_cli_args("");
384 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
385
386 let actual_error = parsed_args.as_ref().err();
387 let expected_error = ProgramExit {
388 message: load_expectation_file_as_string(
389 "no_pos_args_error",
390 ),
391 exit_status: constant::exit_status::GENERIC,
392
393 styled_message: Some(load_expectation_file_as_string(
394 "ansi_no_pos_args_error",
395 )),
396 kind: ExitKind::Error,
397 };
398 let expected_error = Some(&expected_error);
399
400 assert!(actual_error.is_some());
401 assert_eq!(actual_error, expected_error);
402 }
403
404 #[test]
405 fn it_fails_parsing_when_commas_in_pos_args() {
406 let cli_args = parse_cli_args("python,java");
407 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
408
409 let actual_error = parsed_args.as_ref().err();
410 let expected_error = ProgramExit {
411 message: load_expectation_file_as_string(
412 "comma_pos_args_error",
413 ),
414 exit_status: constant::exit_status::GENERIC,
415
416 styled_message: Some(load_expectation_file_as_string(
417 "ansi_comma_pos_args_error",
418 )),
419 kind: ExitKind::Error,
420 };
421 let expected_error = Some(&expected_error);
422
423 assert!(actual_error.is_some());
424 assert_eq!(actual_error, expected_error);
425 }
426
427 #[test]
428 fn it_fails_parsing_when_whitespaces_in_pos_args() {
429 let cli_args = vec![
430 OsString::from(env!("CARGO_PKG_NAME")),
431 OsString::from("r "),
432 ];
433 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
434
435 let actual_error = parsed_args.as_ref().err();
436 let expected_error = ProgramExit {
437 message: load_expectation_file_as_string(
438 "whitespace_pos_args_error",
439 ),
440 exit_status: constant::exit_status::GENERIC,
441
442 styled_message: Some(load_expectation_file_as_string(
443 "ansi_whitespace_pos_args_error",
444 )),
445 kind: ExitKind::Error,
446 };
447 let expected_error = Some(&expected_error);
448
449 assert!(actual_error.is_some());
450 assert_eq!(actual_error, expected_error);
451 }
452
453 #[test]
454 fn it_fails_parsing_when_commas_and_whitespaces_in_pos_args() {
455 let cli_args = vec![
456 OsString::from(env!("CARGO_PKG_NAME")),
457 OsString::from("r ,"),
458 ];
459 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
460
461 let actual_error = parsed_args.as_ref().err();
462 let expected_error = ProgramExit {
463 message: load_expectation_file_as_string(
464 "comma_whitespace_pos_args_error",
465 ),
466 exit_status: constant::exit_status::GENERIC,
467
468 styled_message: Some(load_expectation_file_as_string(
469 "ansi_comma_whitespace_pos_args_error",
470 )),
471 kind: ExitKind::Error,
472 };
473 let expected_error = Some(&expected_error);
474
475 assert!(actual_error.is_some());
476 assert_eq!(actual_error, expected_error);
477 }
478
479 #[test]
480 fn it_fails_parsing_when_server_url_but_no_pos_args() {
481 let cli_args = parse_cli_args("-s https://test.com");
482 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
483
484 let actual_error = parsed_args.as_ref().err();
485 let expected_error = ProgramExit {
486 message: load_expectation_file_as_string(
487 "server_url_no_pos_args_error",
488 ),
489 exit_status: constant::exit_status::GENERIC,
490
491 styled_message: Some(load_expectation_file_as_string(
492 "ansi_server_url_no_pos_args_error",
493 )),
494 kind: ExitKind::Error,
495 };
496 let expected_error = Some(&expected_error);
497
498 assert!(actual_error.is_some());
499 assert_eq!(actual_error, expected_error);
500 }
501
502 #[test]
503 fn it_fails_parsing_when_endpoint_uri_but_no_pos_args() {
504 let cli_args = parse_cli_args("-e /test/api");
505 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
506
507 let actual_error = parsed_args.as_ref().err();
508 let expected_error = ProgramExit {
509 message: load_expectation_file_as_string(
510 "endpoint_uri_no_pos_args_error",
511 ),
512 exit_status: constant::exit_status::GENERIC,
513
514 styled_message: Some(load_expectation_file_as_string(
515 "ansi_endpoint_uri_no_pos_args_error",
516 )),
517 kind: ExitKind::Error,
518 };
519 let expected_error = Some(&expected_error);
520
521 assert!(actual_error.is_some());
522 assert_eq!(actual_error, expected_error);
523 }
524
525 #[test]
526 fn it_fails_parsing_when_inexistent_cli_option() {
527 let cli_args = parse_cli_args("-x");
528 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
529
530 let actual_error = parsed_args.as_ref().err();
531 let expected_error = ProgramExit {
532 message: load_expectation_file_as_string(
533 "unexpected_argument_error",
534 ),
535 exit_status: constant::exit_status::GENERIC,
536 styled_message: Some(load_expectation_file_as_string(
537 "ansi_unexpected_argument_error",
538 )),
539 kind: ExitKind::Error,
540 };
541 let expected_error = Some(&expected_error);
542
543 assert!(actual_error.is_some());
544 assert_eq!(actual_error, expected_error);
545 }
546 }
547 }
548
549 mod parse {
550 use super::*;
551
552 mod success {
553 use super::*;
554
555 #[test]
556 fn it_parses_given_cli_options() {
557 let cli_args = parse_cli_args("rust python -s test -e foo");
558
559 let actual_result = ClapArgsParser::new().parse(cli_args);
560 let expected_result = Args::default()
561 .with_template_names(make_string_vec("rust python"))
562 .with_server_url("test")
563 .with_endpoint_uri("foo");
564
565 assert_eq!(actual_result, expected_result);
566 }
567 }
568 }
569 }
570}