#[cfg(any(feature = "view", feature = "help"))]
use std::ffi::OsString;
use clap::{ColorChoice, Command};
#[cfg(any(feature = "view", feature = "help"))]
use crate::{ColorMode, OutputFormat};
#[cfg(any(feature = "view", feature = "help"))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct ParsedOutput {
pub(crate) format: OutputFormat,
pub(crate) color: ColorMode,
}
#[cfg(any(feature = "view", feature = "help"))]
pub(crate) fn parsed_output<C: clap::CommandFactory>(raw: &[OsString]) -> ParsedOutput {
let command = C::command()
.arg_required_else_help(false)
.args_override_self(true)
.disable_help_flag(true)
.disable_version_flag(true)
.ignore_errors(true)
.color(ColorChoice::Never);
let Ok(matches) = command.try_get_matches_from(raw) else {
return ParsedOutput {
format: OutputFormat::Pretty,
color: ColorMode::Auto,
};
};
let format = matches
.try_get_one::<OutputFormat>("format")
.ok()
.flatten()
.copied()
.unwrap_or_default();
let color = matches
.try_get_one::<ColorMode>("color")
.ok()
.flatten()
.copied()
.unwrap_or_default();
let no_color = matches
.try_get_one::<bool>("no_color")
.ok()
.flatten()
.copied()
.unwrap_or(false);
ParsedOutput {
format,
color: crate::flags::resolve_color(color, no_color),
}
}
#[cfg(feature = "help")]
pub(crate) fn wants_help<C: clap::CommandFactory>(raw: &[OsString]) -> bool {
let command = apply_defaults(C::command()).color(ColorChoice::Never);
matches!(
command.try_get_matches_from(raw),
Err(error) if error.kind() == clap::error::ErrorKind::DisplayHelp
)
}
#[cfg(feature = "help")]
pub(crate) fn requires_input<C: clap::CommandFactory>() -> bool {
let command = C::command();
command.is_arg_required_else_help_set() || command.is_subcommand_required_set()
}
#[must_use]
pub fn apply_defaults(command: Command) -> Command {
assert_help_enabled(&command);
command
.args_override_self(true)
.color(ColorChoice::Auto)
.disable_help_flag(false)
.disable_version_flag(false)
}
pub fn verify<C: clap::CommandFactory>() {
C::command().debug_assert();
assert_help_enabled(&C::command());
}
pub fn assert_help_enabled(command: &Command) {
assert!(
!command.is_disable_help_flag_set(),
"disable_help_flag is forbidden; ctl-core owns -h/--help"
);
assert!(
!command.is_disable_version_flag_set(),
"disable_version_flag is forbidden; ctl-core owns -V/--version"
);
}
#[cfg(test)]
mod tests {
use clap::{Command, CommandFactory, Parser};
use super::{apply_defaults, assert_help_enabled};
#[derive(Parser)]
#[command(version, about = "toy", arg_required_else_help = true)]
struct Toy {
#[command(subcommand)]
command: ToyCmd,
}
#[derive(clap::Subcommand)]
enum ToyCmd {
Status,
}
#[test]
fn verify_toy() {
super::verify::<Toy>();
}
#[test]
fn derive_keeps_help_and_version() {
let mut command = apply_defaults(Toy::command());
assert_help_enabled(&command);
assert!(command.is_args_override_self());
let help = command.render_long_help().to_string();
assert!(help.contains("-h, --help"));
assert!(help.contains("-V, --version"));
}
#[test]
fn preserves_bare_invocation_policy() {
let command = apply_defaults(Toy::command().arg_required_else_help(false));
assert!(!command.is_arg_required_else_help_set());
}
#[test]
#[should_panic(expected = "disable_help_flag is forbidden")]
fn rejects_disabled_help() {
let command = Command::new("x").disable_help_flag(true);
assert_help_enabled(&command);
}
}