#![allow(
clippy::struct_excessive_bools,
clippy::too_many_lines,
clippy::unused_self,
// Remaining hits are `root_settings: Default::default()` in test
// graph fixtures, where the typed-default suggestion is MaybeIncorrect.
clippy::default_trait_access
)]
use std::process::ExitCode;
use clap::FromArgMatches;
pub use crate::cli::Cli;
use crate::term_setup::{EarlyTerminalState, resolve_early_terminal_state};
mod cli;
mod command_list;
mod completions;
mod diagnostic_registry;
mod error_rendering;
mod help_rendering;
mod manpages;
mod port_subcommand;
mod stamp;
mod term_setup;
mod version_info;
pub(crate) fn plural(n: usize) -> &'static str {
if n == 1 { "" } else { "s" }
}
pub(crate) fn print_pretty_json<T>(value: &T, error_context: &'static str) -> anyhow::Result<()>
where
T: serde::Serialize + ?Sized,
{
use anyhow::Context;
let body = serde_json::to_string_pretty(value).context(error_context)?;
println!("{body}");
Ok(())
}
#[derive(Debug, Clone)]
pub(crate) struct SubcommandRow {
pub(crate) tokens: Vec<String>,
pub(crate) about: String,
}
pub(crate) fn row_from_subcommand(sub: &clap::Command) -> SubcommandRow {
let mut tokens = vec![sub.get_name().to_owned()];
for alias in sub.get_visible_aliases() {
tokens.push(alias.to_string());
}
let about = sub
.get_about()
.map(|s| s.to_string().lines().next().unwrap_or("").trim().to_owned())
.unwrap_or_default();
SubcommandRow { tokens, about }
}
pub(crate) fn rows_display_width(rows: &[SubcommandRow]) -> usize {
rows.iter()
.map(|row| row.tokens.join(", ").len())
.max()
.unwrap_or(0)
}
pub fn run<I, T>(args: I) -> ExitCode
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
{
let arguments: Vec<std::ffi::OsString> = args.into_iter().map(Into::into).collect();
if let Some(code) = stamp::dispatch(&arguments) {
return code;
}
let cmd = help_rendering::prepare_top_level_command();
let matches = match cmd.try_get_matches_from(arguments) {
Ok(m) => m,
Err(err) => {
err.exit();
}
};
let dots_shortcut = matches.subcommand_name() == Some(help_rendering::DOTS_HINT);
let mut parsed = match Cli::from_arg_matches(&matches) {
Ok(cli) => cli,
Err(err) => err.exit(),
};
if dots_shortcut {
parsed.list = true;
}
let EarlyTerminalState { color, reporter } =
match resolve_early_terminal_state(parsed.color, parsed.verbose, parsed.quiet) {
Ok(state) => state,
Err(exit_code) => return exit_code,
};
match cli::run(parsed, reporter, color) {
Ok(code) => code,
Err(error) => {
error_rendering::render_error(&error, color);
ExitCode::FAILURE
}
}
}