1use std::path::{Path, PathBuf};
2
3use clap::builder::PossibleValuesParser;
4
5use crate::serde::args::{ArgType, CliArgsSet};
6
7#[derive(Debug, Clone, Copy)]
8pub enum OutputLocation<'a> {
9 File(&'a Path),
10 Folder(&'a Path),
11}
12
13#[derive(clap::Args, Debug)]
14#[group(multiple = false, required = true)]
15pub struct Output {
16 #[arg(short = 'o', long = "output-file")]
19 pub file: Option<PathBuf>,
20
21 #[arg(short = 'd', long = "output-folder")]
24 pub directory: Option<PathBuf>,
25}
26
27impl Output {
28 pub fn location(&self) -> OutputLocation<'_> {
29 match (&self.directory, &self.file) {
30 (Some(dir), None) => OutputLocation::Folder(dir),
31 (None, Some(file)) => OutputLocation::File(file),
32 (None, None) => panic!("got neither a file nor a directory; clap should prevent this"),
33 (Some(dir), Some(file)) => {
34 panic!("got both file '{file:?}' and directory '{dir:?}'; clap should prevent this")
35 }
36 }
37 }
38}
39
40#[derive(clap::Parser, Debug)]
41#[command(args_conflicts_with_subcommands = true, subcommand_negates_reqs = true)]
42pub struct StandardArgs {
43 #[command(subcommand)]
44 pub subcommand: Option<Command>,
45
46 #[arg(short, long, visible_alias("config-file"))]
48 pub config: Option<PathBuf>,
49
50 #[arg(num_args(1..), required=true)]
52 pub directories: Vec<PathBuf>,
53
54 #[arg(long, exclusive(true))]
55 pub completions: Option<String>,
56
57 #[command(flatten)]
58 pub output: Output,
59
60 #[arg(long, num_args=1..)]
72 pub target_os: Option<Vec<String>>,
73}
74
75#[derive(Debug, Clone, Copy, clap::Subcommand)]
76pub enum Command {
77 Completions {
79 shell: clap_complete::Shell,
81 },
82}
83
84#[derive(Debug, Clone, Default)]
85#[non_exhaustive]
86pub struct PersonalizeClap {
87 name: Option<&'static str>,
88 version: Option<&'static str>,
89 author: Option<&'static str>,
90 about: Option<&'static str>,
91}
92
93impl PersonalizeClap {
94 pub const fn new() -> Self {
95 Self {
96 name: None,
97 version: None,
98 author: None,
99 about: None,
100 }
101 }
102
103 pub const fn name(self, name: &'static str) -> Self {
104 Self {
105 name: Some(name),
106 ..self
107 }
108 }
109
110 pub const fn version(self, version: &'static str) -> Self {
111 Self {
112 version: Some(version),
113 ..self
114 }
115 }
116
117 pub const fn author(self, author: &'static str) -> Self {
118 Self {
119 author: Some(author),
120 ..self
121 }
122 }
123
124 pub const fn about(self, about: &'static str) -> Self {
125 Self {
126 about: Some(about),
127 ..self
128 }
129 }
130}
131
132pub fn add_personalizations(
133 command: clap::Command,
134 personalizations: PersonalizeClap,
135) -> clap::Command {
136 let command = command.arg(
137 clap::Arg::new("version")
138 .short('V')
139 .long("version")
140 .action(clap::ArgAction::Version),
141 );
142
143 let command = match personalizations.name {
144 Some(name) => command.name(name),
145 None => command,
146 };
147
148 let command = match personalizations.version {
149 Some(version) => command.version(version),
150 None => command,
151 };
152
153 let command = match personalizations.author {
154 Some(author) => command.author(author),
155 None => command,
156 };
157
158 let command = match personalizations.about {
159 Some(about) => command.about(about),
160 None => command,
161 };
162
163 command
164}
165
166pub fn add_lang_argument(command: clap::Command, languages: &[&'static str]) -> clap::Command {
169 let arg = clap::Arg::new("language")
170 .short('l')
171 .long("lang")
172 .value_name("LANGUAGE")
173 .value_parser(PossibleValuesParser::new(languages))
174 .action(clap::ArgAction::Set)
175 .help("the output language of generated types");
176
177 command.arg(match languages {
178 [] => panic!("need at least one language"),
179 [lang] => arg.required(false).default_value(lang),
180 _ => arg.required(true),
181 })
182}
183
184pub fn add_language_params_to_clap(
188 command: clap::Command,
189 language: &'static str,
190 args: &CliArgsSet,
191) -> clap::Command {
192 if let Some(arg) = command
193 .get_arguments()
194 .find(|arg| arg.get_id().as_str().starts_with(language))
195 {
196 panic!(
197 "existing argument {id:?} conflicts with language {language}",
198 id = arg.get_id().as_str(),
199 )
200 }
201
202 args.iter().fold(command, |command, spec| {
203 let arg = clap::Arg::new(spec.full_key.to_owned())
204 .long(spec.full_key.to_owned())
205 .required(false);
206
207 command.arg(match spec.arg_type {
208 ArgType::Bool => arg.action(clap::ArgAction::SetTrue),
209 ArgType::Value => arg.action(clap::ArgAction::Set).value_name(spec.key),
210 })
211 })
212}