1use std::process::ExitCode;
4
5use anyhow::Result;
6#[cfg(feature = "help")]
7use clap::CommandFactory;
8
9use crate::color::ColorMode;
10use crate::format::OutputFormat;
11
12#[cfg(feature = "help")]
14#[must_use]
15pub fn go<C: clap::Parser + CommandFactory>(
16 bin: &str,
17 body: impl FnOnce(C) -> Result<()>,
18) -> ExitCode {
19 main_with_help::<C>(bin, || {
20 let raw: Vec<std::ffi::OsString> = std::env::args_os().collect();
21 let words: Vec<String> = raw
22 .iter()
23 .skip(1)
24 .map(|arg| arg.to_string_lossy().into_owned())
25 .collect();
26 let warnings = crate::flags::chassis_warnings(words.iter().map(String::as_str));
27 crate::flags::emit_warnings(bin, &warnings);
28 let matches = legacy_command::<C>().get_matches_from(&raw);
29 let cli = C::from_arg_matches(&matches).unwrap_or_else(|err| err.exit());
30 body(cli)
31 })
32}
33
34#[cfg(feature = "help")]
35fn legacy_command<C: CommandFactory>() -> clap::Command {
36 crate::parser::apply_defaults(C::command()).arg_required_else_help(true)
37}
38
39#[must_use]
41pub fn main(bin: &str, body: impl FnOnce() -> Result<()>) -> ExitCode {
42 main_with(bin, OutputFormat::Pretty, ColorMode::Auto, body)
43}
44
45#[must_use]
47pub fn main_with(
48 bin: &str,
49 format: OutputFormat,
50 color: ColorMode,
51 body: impl FnOnce() -> Result<()>,
52) -> ExitCode {
53 match body() {
54 Ok(()) => ExitCode::SUCCESS,
55 Err(error) => {
56 #[cfg(feature = "view")]
57 {
58 let _ = crate::view::View::new(format, color).emit_err(bin, &format!("{error:#}"));
59 }
60 #[cfg(not(feature = "view"))]
61 {
62 let _ = (format, color);
63 eprintln!("{bin}: {error:#}");
64 }
65 ExitCode::FAILURE
66 }
67 }
68}
69
70#[cfg(feature = "help")]
72#[must_use]
73pub fn main_with_help<C: CommandFactory>(bin: &str, body: impl FnOnce() -> Result<()>) -> ExitCode {
74 let raw = std::env::args_os().collect::<Vec<_>>();
75 if raw.len() == 1 && crate::parser::requires_input::<C>() {
76 return crate::help::emit_bare::<C>(ColorMode::Auto)
77 .map_or(ExitCode::FAILURE, |()| ExitCode::from(2));
78 }
79 match crate::help::try_emit::<C>() {
80 Ok(true) => return ExitCode::SUCCESS,
81 Ok(false) => {}
82 Err(_) => return ExitCode::FAILURE,
83 }
84 main(bin, body)
85}
86
87#[cfg(all(test, feature = "help"))]
88mod tests {
89 use clap::Parser;
90
91 use super::legacy_command;
92
93 #[derive(Parser)]
94 #[command(version)]
95 struct OptionalCli {}
96
97 #[test]
98 fn legacy_go_keeps_bare_help_required() {
99 assert!(legacy_command::<OptionalCli>().is_arg_required_else_help_set());
100 }
101}