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