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 super::*;
175 use crate::{ExitKind, constant};
176
177 #[rstest]
178 #[case("-V")]
179 #[case("--version")]
180 #[case("-V rust")]
181 #[case("rust -V")]
182 #[case("rust -s foo -V")]
183 #[case("rust -e bar -V")]
184 #[case("-aV")]
185 fn it_parses_version_cli_option(#[case] cli_args: &str) {
186 let cli_args = parse_cli_args(cli_args);
187 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
188
189 let actual_error = parsed_args.as_ref().err();
190 let expected_error = ProgramExit {
191 message: format!(
192 "{} {}",
193 env!("CARGO_PKG_NAME"),
194 env!("CARGO_PKG_VERSION")
195 ),
196 exit_status: 0,
197 styled_message: None,
198 kind: ExitKind::VersionInfos,
199 };
200 let expected_error = Some(&expected_error);
201
202 assert!(actual_error.is_some());
203 assert_eq!(actual_error, expected_error);
204 }
205
206 #[rstest]
207 #[case("-h")]
208 #[case("--help")]
209 #[case("-h rust")]
210 #[case("rust -h")]
211 #[case("rust -s foo -h")]
212 #[case("rust -e bar -h")]
213 #[case("-aVh")]
214 fn it_parses_help_cli_option(#[case] cli_args: &str) {
215 let cli_args = parse_cli_args(cli_args);
216 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
217
218 let actual_error = parsed_args.as_ref().err();
219 let expected_error = ProgramExit {
220 message: get_help_message(),
221 exit_status: 0,
222 styled_message: Some(get_ansi_help_message()),
223 kind: ExitKind::HelpInfos,
224 };
225 let expected_error = Some(&expected_error);
226
227 assert!(actual_error.is_some());
228 assert_eq!(actual_error, expected_error);
229 }
230
231 #[rstest]
232 #[case("-a")]
233 #[case("--author")]
234 #[case("-a rust")]
235 #[case("rust -a")]
236 #[case("rust -s foo -a")]
237 #[case("rust -e bar -a")]
238 fn it_parses_author_cli_option_preemptively(
239 #[case] cli_args: &str,
240 ) {
241 let cli_args = parse_cli_args(cli_args);
242 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
243
244 let actual_error = parsed_args.as_ref().err();
245 let expected_error = ProgramExit {
246 message: env!("CARGO_PKG_AUTHORS").to_string(),
247 exit_status: 0,
248 styled_message: None,
249 kind: ExitKind::AuthorInfos,
250 };
251 let expected_error = Some(&expected_error);
252
253 assert!(actual_error.is_some());
254 assert_eq!(actual_error, expected_error);
255 }
256
257 #[rstest]
258 #[case("rust")]
259 #[case("rust python node")]
260 fn it_parses_pos_args_without_server_url_cli_option(
261 #[case] cli_options: &str,
262 ) {
263 let cli_args = parse_cli_args(cli_options);
264 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
265
266 let actual_result = parsed_args.as_ref().ok();
267 let expected_result = Args::default()
268 .with_template_names(make_string_vec(cli_options))
269 .with_server_url(constant::template_generator::BASE_URL)
270 .with_endpoint_uri(constant::template_generator::URI);
271 let expected_result = Some(&expected_result);
272
273 println!("{:?}", parsed_args);
274 assert!(actual_result.is_some());
275 assert_eq!(actual_result, expected_result);
276 }
277
278 #[rstest]
279 #[case("rust -s https://test.com")]
280 #[case("rust --server-url https://test.com")]
281 fn it_parses_pos_args_with_server_url_cli_option(
282 #[case] cli_args: &str,
283 ) {
284 let cli_args = parse_cli_args(cli_args);
285 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
286
287 let actual_result = parsed_args.as_ref().ok();
288 let expected_result = Args::default()
289 .with_template_names(make_string_vec("rust"))
290 .with_server_url("https://test.com")
291 .with_endpoint_uri(constant::template_generator::URI);
292 let expected_result = Some(&expected_result);
293
294 assert!(actual_result.is_some());
295 assert_eq!(actual_result, expected_result);
296 }
297
298 #[rstest]
299 #[case("rust -e /test/api")]
300 #[case("rust --endpoint-uri /test/api")]
301 fn it_parses_pos_args_with_endpoint_uri_cli_option(
302 #[case] cli_args: &str,
303 ) {
304 let cli_args = parse_cli_args(cli_args);
305 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
306
307 let actual_result = parsed_args.as_ref().ok();
308 let expected_result = Args::default()
309 .with_template_names(make_string_vec("rust"))
310 .with_server_url(constant::template_generator::BASE_URL)
311 .with_endpoint_uri("/test/api");
312 let expected_result = Some(&expected_result);
313
314 assert!(actual_result.is_some());
315 assert_eq!(actual_result, expected_result);
316 }
317 }
318
319 mod failure {
320 use super::*;
321 use crate::{ExitKind, constant};
322
323 #[test]
324 fn it_fails_parsing_when_no_pos_args_given() {
325 let cli_args = parse_cli_args("");
326 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
327
328 let actual_error = parsed_args.as_ref().err();
329 let expected_error = ProgramExit {
330 message: load_expectation_file_as_string(
331 "no_pos_args_error",
332 ),
333 exit_status: constant::exit_status::GENERIC,
334
335 styled_message: Some(load_expectation_file_as_string(
336 "ansi_no_pos_args_error",
337 )),
338 kind: ExitKind::Error,
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 #[test]
347 fn it_fails_parsing_when_commas_in_pos_args() {
348 let cli_args = parse_cli_args("python,java");
349 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
350
351 let actual_error = parsed_args.as_ref().err();
352 let expected_error = ProgramExit {
353 message: load_expectation_file_as_string(
354 "comma_pos_args_error",
355 ),
356 exit_status: constant::exit_status::GENERIC,
357
358 styled_message: Some(load_expectation_file_as_string(
359 "ansi_comma_pos_args_error",
360 )),
361 kind: ExitKind::Error,
362 };
363 let expected_error = Some(&expected_error);
364
365 assert!(actual_error.is_some());
366 assert_eq!(actual_error, expected_error);
367 }
368
369 #[test]
370 fn it_fails_parsing_when_whitespaces_in_pos_args() {
371 let cli_args = vec![
372 OsString::from(env!("CARGO_PKG_NAME")),
373 OsString::from("r "),
374 ];
375 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
376
377 let actual_error = parsed_args.as_ref().err();
378 let expected_error = ProgramExit {
379 message: load_expectation_file_as_string(
380 "whitespace_pos_args_error",
381 ),
382 exit_status: constant::exit_status::GENERIC,
383
384 styled_message: Some(load_expectation_file_as_string(
385 "ansi_whitespace_pos_args_error",
386 )),
387 kind: ExitKind::Error,
388 };
389 let expected_error = Some(&expected_error);
390
391 assert!(actual_error.is_some());
392 assert_eq!(actual_error, expected_error);
393 }
394
395 #[test]
396 fn it_fails_parsing_when_commas_and_whitespaces_in_pos_args() {
397 let cli_args = vec![
398 OsString::from(env!("CARGO_PKG_NAME")),
399 OsString::from("r ,"),
400 ];
401 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
402
403 let actual_error = parsed_args.as_ref().err();
404 let expected_error = ProgramExit {
405 message: load_expectation_file_as_string(
406 "comma_whitespace_pos_args_error",
407 ),
408 exit_status: constant::exit_status::GENERIC,
409
410 styled_message: Some(load_expectation_file_as_string(
411 "ansi_comma_whitespace_pos_args_error",
412 )),
413 kind: ExitKind::Error,
414 };
415 let expected_error = Some(&expected_error);
416
417 assert!(actual_error.is_some());
418 assert_eq!(actual_error, expected_error);
419 }
420
421 #[test]
422 fn it_fails_parsing_when_server_url_but_no_pos_args() {
423 let cli_args = parse_cli_args("-s https://test.com");
424 let parsed_args = ClapArgsParser::new().try_parse(cli_args);
425
426 let actual_error = parsed_args.as_ref().err();
427 let expected_error = ProgramExit {
428 message: load_expectation_file_as_string(
429 "server_url_no_pos_args_error",
430 ),
431 exit_status: constant::exit_status::GENERIC,
432
433 styled_message: Some(load_expectation_file_as_string(
434 "ansi_server_url_no_pos_args_error",
435 )),
436 kind: ExitKind::Error,
437 };
438 let expected_error = Some(&expected_error);
439
440 assert!(actual_error.is_some());
441 assert_eq!(actual_error, expected_error);
442 }
443
444 #[test]
445 fn it_fails_parsing_when_endpoint_uri_but_no_pos_args() {
446 let cli_args = parse_cli_args("-e /test/api");
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 "endpoint_uri_no_pos_args_error",
453 ),
454 exit_status: constant::exit_status::GENERIC,
455
456 styled_message: Some(load_expectation_file_as_string(
457 "ansi_endpoint_uri_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_inexistent_cli_option() {
469 let cli_args = parse_cli_args("-x");
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 "unexpected_argument_error",
476 ),
477 exit_status: constant::exit_status::GENERIC,
478 styled_message: Some(load_expectation_file_as_string(
479 "ansi_unexpected_argument_error",
480 )),
481 kind: ExitKind::Error,
482 };
483 let expected_error = Some(&expected_error);
484
485 assert!(actual_error.is_some());
486 assert_eq!(actual_error, expected_error);
487 }
488 }
489 }
490 }
491}