1use std::io::Read;
2
3use clap::{Args, Parser, Subcommand};
4
5const HELP_TEMPLATE: &str = "\
6{before-help}{name} {version}
7{about-with-newline}{author-with-newline}
8{usage-heading} {usage}
9
10{all-args}{after-help}
11";
12
13#[derive(Parser, Debug)]
14#[command(author, version, about, long_about = None, propagate_version = true, help_template = HELP_TEMPLATE)]
15pub struct Cli {
16 #[command(subcommand)]
17 pub command: Commands,
18
19 #[arg(long, value_enum, global = true, default_value_t)]
20 pub log_level: LogLevel,
21}
22
23#[derive(Subcommand, Debug)]
24pub enum Commands {
25 Format(FormatCommandArguments),
27
28 Verify(VerifyCommandArguments),
30
31 Init(InitCommandArguments),
33
34 Completions(CompletionsCommandArguments),
36
37 CachePrune,
39}
40
41#[derive(
42 Clone, Copy, PartialEq, Eq, Debug, Default, serde::Serialize, serde::Deserialize, Hash,
43)]
44#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
45pub enum OnMissingToolBinary {
46 #[default]
48 #[serde(rename = "ignore")]
49 Ignore,
50
51 #[serde(rename = "fail")]
53 Fail,
54
55 #[serde(rename = "fail-fast")]
57 FailFast,
58}
59
60impl clap::ValueEnum for OnMissingToolBinary {
61 #[inline]
62 fn value_variants<'a>() -> &'a [Self] {
63 &[Self::Ignore, Self::Fail, Self::FailFast]
64 }
65
66 #[inline]
67 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
68 Some(match self {
69 Self::Fail => clap::builder::PossibleValue::new("fail"),
70 Self::FailFast => clap::builder::PossibleValue::new("fail-fast"),
71 Self::Ignore => clap::builder::PossibleValue::new("ignore"),
72 })
73 }
74}
75
76#[derive(
77 Clone, Copy, PartialEq, Eq, Debug, Default, serde::Serialize, serde::Deserialize, Hash,
78)]
79#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
80pub enum OnMissingLanguageDefinition {
81 #[default]
83 #[serde(rename = "ignore")]
84 Ignore,
85
86 #[serde(rename = "fail")]
88 Fail,
89
90 #[serde(rename = "fail-fast")]
92 FailFast,
93}
94
95impl clap::ValueEnum for OnMissingLanguageDefinition {
96 #[inline]
97 fn value_variants<'a>() -> &'a [Self] {
98 &[Self::Ignore, Self::Fail, Self::FailFast]
99 }
100
101 #[inline]
102 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
103 Some(match self {
104 Self::Fail => clap::builder::PossibleValue::new("fail"),
105 Self::FailFast => clap::builder::PossibleValue::new("fail-fast"),
106 Self::Ignore => clap::builder::PossibleValue::new("ignore"),
107 })
108 }
109}
110
111#[derive(Args, Debug)]
112pub struct FormatCommandArguments {
113 #[arg()]
115 pub input: Vec<std::path::PathBuf>,
116
117 #[arg(long, default_value_t = false)]
119 pub stdin: bool,
120
121 #[arg(long)]
123 pub config: Option<std::path::PathBuf>,
124
125 #[arg(long, default_value_t = false)]
127 pub debug: bool,
128
129 #[arg(long)]
133 pub threads: Option<usize>,
134
135 #[arg(long, default_value_t = false)]
137 pub cache: bool,
138
139 #[arg(long)]
143 pub timeout: Option<u64>,
144
145 #[arg(long)]
149 pub on_missing_language_definition: Option<OnMissingLanguageDefinition>,
150
151 #[arg(long)]
155 pub on_missing_tool_binary: Option<OnMissingToolBinary>,
156}
157
158#[derive(Args, Debug)]
159pub struct VerifyCommandArguments {
160 #[arg()]
162 pub input: Vec<std::path::PathBuf>,
163
164 #[arg(long, default_value_t = false)]
166 pub stdin: bool,
167
168 #[arg(long)]
170 pub config: Option<std::path::PathBuf>,
171
172 #[arg(long, default_value_t = false)]
174 pub debug: bool,
175
176 #[arg(long)]
180 pub threads: Option<usize>,
181
182 #[arg(long)]
186 pub timeout: Option<u64>,
187
188 #[arg(long)]
192 pub on_missing_language_definition: Option<OnMissingLanguageDefinition>,
193
194 #[arg(long)]
198 pub on_missing_tool_binary: Option<OnMissingToolBinary>,
199}
200
201impl From<VerifyCommandArguments> for FormatCommandArguments {
202 #[inline]
203 fn from(value: VerifyCommandArguments) -> Self {
204 Self {
205 cache: false,
206 config: value.config,
207 debug: value.debug,
208 input: value.input,
209 on_missing_language_definition: value.on_missing_language_definition,
210 on_missing_tool_binary: value.on_missing_tool_binary,
211 stdin: value.stdin,
212 threads: value.threads,
213 timeout: value.timeout,
214 }
215 }
216}
217
218#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
219pub enum ConfigFileFormat {
220 #[default]
221 Json,
222 Json5,
223 JsonC,
224 Toml,
225 Yaml,
226}
227
228impl ConfigFileFormat {
229 #[inline]
230 pub const fn extension(&self) -> &'static str {
231 match self {
232 Self::Json => "json",
233 Self::Json5 => "json5",
234 Self::JsonC => "jsonc",
235 Self::Toml => "toml",
236 Self::Yaml => "yml",
237 }
238 }
239}
240
241impl core::fmt::Display for ConfigFileFormat {
242 #[inline]
243 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
244 match self {
245 Self::Json => write!(f, "json"),
246 Self::Json5 => write!(f, "json5"),
247 Self::JsonC => write!(f, "jsonc"),
248 Self::Toml => write!(f, "toml"),
249 Self::Yaml => write!(f, "yaml"),
250 }
251 }
252}
253
254impl clap::ValueEnum for ConfigFileFormat {
255 #[inline]
256 fn value_variants<'a>() -> &'a [Self] {
257 &[Self::Json, Self::Json5, Self::JsonC, Self::Toml, Self::Yaml]
258 }
259
260 #[inline]
261 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
262 Some(match self {
263 Self::Json => clap::builder::PossibleValue::new("json"),
264 Self::Json5 => clap::builder::PossibleValue::new("json5").hide(true),
265 Self::JsonC => clap::builder::PossibleValue::new("jsonc").hide(true),
266 Self::Toml => clap::builder::PossibleValue::new("toml"),
267 Self::Yaml => clap::builder::PossibleValue::new("yaml").alias("yml"),
268 })
269 }
270}
271
272#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
273pub enum InitCommandSchemaVersion {
274 #[default]
276 Locked,
277
278 Stable,
280
281 Development,
283}
284
285impl clap::ValueEnum for InitCommandSchemaVersion {
286 #[inline]
287 fn value_variants<'a>() -> &'a [Self] {
288 &[Self::Locked, Self::Stable, Self::Development]
289 }
290
291 #[inline]
292 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
293 Some(match self {
294 Self::Development => clap::builder::PossibleValue::new("development"),
295 Self::Locked => clap::builder::PossibleValue::new("locked"),
296 Self::Stable => clap::builder::PossibleValue::new("stable"),
297 })
298 }
299}
300
301impl core::fmt::Display for InitCommandSchemaVersion {
302 #[inline]
303 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
304 match self {
305 Self::Development => write!(f, "development"),
306 Self::Locked => write!(f, "locked"),
307 Self::Stable => write!(f, "stable"),
308 }
309 }
310}
311
312#[derive(Args, Debug)]
313pub struct InitCommandArguments {
314 #[arg(long, default_value_t = false)]
316 pub force: bool,
317
318 #[arg(long, default_value_t)]
319 pub format: ConfigFileFormat,
320
321 #[arg(long, default_value_t)]
322 pub schema_version: InitCommandSchemaVersion,
323}
324
325#[derive(clap::ValueEnum, Clone, Copy, PartialEq, Eq, Debug, Default)]
326pub enum LogLevel {
327 Trace,
328 #[default]
329 Debug,
330 Info,
331 Warn,
332 Error,
333 Off,
334}
335
336#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
337pub enum Shell {
338 Bash,
340
341 Elvish,
343
344 Fish,
346
347 Nushell,
349
350 PowerShell,
352
353 Zsh,
355}
356
357impl clap::ValueEnum for Shell {
358 #[inline]
359 fn value_variants<'a>() -> &'a [Self] {
360 &[
361 Self::Bash,
362 Self::Elvish,
363 Self::Fish,
364 Self::Nushell,
365 Self::PowerShell,
366 Self::Zsh,
367 ]
368 }
369
370 #[inline]
371 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
372 Some(match self {
373 Self::Bash => clap::builder::PossibleValue::new("bash"),
374 Self::Elvish => clap::builder::PossibleValue::new("elvish"),
375 Self::Fish => clap::builder::PossibleValue::new("fish"),
376 Self::Nushell => clap::builder::PossibleValue::new("nushell"),
377 Self::PowerShell => clap::builder::PossibleValue::new("powershell"),
378 Self::Zsh => clap::builder::PossibleValue::new("zsh"),
379 })
380 }
381}
382
383#[derive(Args, Debug)]
384pub struct CompletionsCommandArguments {
385 pub shell: Shell,
386}
387
388#[derive(Args, Debug)]
389pub struct CachePruneArguments {}
390
391#[inline]
392pub fn read_stdin() -> std::io::Result<String> {
393 let stdin = std::io::stdin();
394
395 let mut input = String::new();
396
397 stdin.lock().read_to_string(&mut input)?;
398
399 Ok(input)
400}