gitignore_template_generator/parser/
impls.rs1use std::{ffi::OsString, process::exit};
2
3use clap::{ArgMatches, Command};
4
5use super::{Args, ArgsParser, command::build_clap_args};
6use crate::{ExitKind, ProgramExit, constant};
7
8pub struct ClapArgsParser {
11 cli_parser: Command,
12}
13
14#[allow(clippy::new_without_default)]
15impl ClapArgsParser {
16 pub fn new() -> Self {
17 Self {
18 cli_parser: Command::new(env!("CARGO_PKG_NAME"))
19 .version(env!("CARGO_PKG_VERSION"))
20 .author(env!("CARGO_PKG_AUTHORS"))
21 .about(constant::parser_infos::ABOUT)
22 .help_template(include_str!("../../assets/help_template.txt"))
23 .disable_help_flag(true)
24 .disable_version_flag(true)
25 .args(build_clap_args()),
26 }
27 }
28
29 fn parse_global_options(&self, args: &Args) -> Option<ProgramExit> {
30 if args.show_help {
31 let rendered_help = self.cli_parser.clone().render_help();
32 Some(ProgramExit {
33 message: rendered_help.to_string().trim_end().to_string(),
34 exit_status: constant::exit_status::SUCCESS,
35 styled_message: Some(
36 rendered_help.ansi().to_string().trim_end().to_string(),
37 ),
38 kind: ExitKind::HelpInfos,
39 })
40 } else if args.show_version {
41 let message = match self.cli_parser.get_version() {
42 Some(version) => {
43 format!("{} {version}", env!("CARGO_PKG_NAME"))
44 }
45 None => constant::error_messages::VERSION_INFOS_NOT_AVAILABLE
46 .to_string(),
47 };
48
49 Some(ProgramExit {
50 message,
51 exit_status: constant::exit_status::SUCCESS,
52 styled_message: None,
53 kind: ExitKind::VersionInfos,
54 })
55 } else if args.show_author {
56 let message = String::from(match self.cli_parser.get_author() {
57 Some(author) => author,
58 None => constant::error_messages::AUTHOR_INFOS_NOT_AVAILABLE,
59 });
60
61 Some(ProgramExit {
62 message,
63 exit_status: constant::exit_status::SUCCESS,
64 styled_message: None,
65 kind: ExitKind::AuthorInfos,
66 })
67 } else {
68 None
69 }
70 }
71
72 fn map_arg_matches_to_struct(arg_matches: &ArgMatches) -> Args {
73 Args {
74 template_names: arg_matches
75 .get_many::<String>("TEMPLATE_NAMES")
76 .map(|vals| vals.cloned().collect())
77 .unwrap_or_default(),
78
79 server_url: arg_matches
80 .get_one::<String>("SERVER_URL")
81 .unwrap()
82 .to_string(),
83
84 endpoint_uri: arg_matches
85 .get_one::<String>("ENDPOINT_URI")
86 .unwrap()
87 .to_string(),
88
89 show_help: arg_matches.get_flag("HELP"),
90 show_version: arg_matches.get_flag("VERSION"),
91 show_author: arg_matches.get_flag("AUTHOR"),
92 }
93 }
94
95 fn print_error_message(error: &ProgramExit, message: &str) {
96 match error.kind {
97 ExitKind::VersionInfos
98 | ExitKind::HelpInfos
99 | ExitKind::AuthorInfos => println!("{message}"),
100 ExitKind::Error => eprintln!("{message}"),
101 }
102 }
103}
104
105impl ArgsParser for ClapArgsParser {
106 fn parse(&self, args: impl IntoIterator<Item = OsString>) -> Args {
118 match self.try_parse(args) {
119 Ok(parsed_args) => parsed_args,
120 Err(error) => {
121 if let Some(value) = &error.styled_message {
122 Self::print_error_message(&error, value);
123 } else {
124 Self::print_error_message(&error, &error.message);
125 }
126
127 exit(error.exit_status);
128 }
129 }
130 }
131
132 fn try_parse(
133 &self,
134 args: impl IntoIterator<Item = OsString>,
135 ) -> Result<Args, ProgramExit> {
136 match self.cli_parser.clone().try_get_matches_from(args) {
137 Ok(parsing_result) => {
138 let parsed_args =
139 Self::map_arg_matches_to_struct(&parsing_result);
140 match self.parse_global_options(&parsed_args) {
141 Some(error) => Err(error),
142 None => Ok(parsed_args),
143 }
144 }
145 Err(error) => Err(ProgramExit {
146 message: format!(
147 "{}\nFor more information, try '--help'.",
148 error.render()
149 ),
150 exit_status: error.exit_code(),
151 styled_message: Some(format!(
152 "{}\nFor more information, try '\u{1b}[1m--help\u{1b}[0m'.",
153 error.render().ansi()
154 )),
155 kind: ExitKind::Error,
156 }),
157 }
158 }
159}