use std::ffi::OsString;
use clap::error::ErrorKind;
pub use nils_common::cli_contract::{
Envelope, EnvelopeError, OutputFormat, emit_parse_error, exit, schema_version_for,
};
pub fn handle_parse_error<I>(binary: &str, argv: I, err: clap::Error) -> i32
where
I: IntoIterator<Item = OsString>,
{
let kind = err.kind();
if matches!(
kind,
ErrorKind::DisplayHelp
| ErrorKind::DisplayVersion
| ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
) {
let _ = err.print();
return err.exit_code();
}
let argv: Vec<OsString> = argv.into_iter().collect();
let format = detect_format_from_argv(&argv);
let code = match kind {
ErrorKind::InvalidSubcommand => "unknown-subcommand",
_ => "parse-error",
};
let message = render_clap_message(&err);
emit_parse_error(binary, format, code, &message)
}
fn detect_format_from_argv(argv: &[OsString]) -> OutputFormat {
let mut iter = argv.iter().skip(1);
while let Some(arg) = iter.next() {
let arg = arg.to_string_lossy();
if arg == "--json" {
return OutputFormat::Json;
}
if arg == "--format"
&& let Some(next) = iter.next()
&& next.to_string_lossy().eq_ignore_ascii_case("json")
{
return OutputFormat::Json;
}
if let Some(rest) = arg.strip_prefix("--format=")
&& rest.eq_ignore_ascii_case("json")
{
return OutputFormat::Json;
}
}
OutputFormat::Text
}
fn render_clap_message(err: &clap::Error) -> String {
err.to_string()
.lines()
.find(|line| !line.trim().is_empty())
.map(|line| {
let line = line.trim();
line.strip_prefix("error:")
.map(str::trim)
.unwrap_or(line)
.to_string()
})
.unwrap_or_else(|| "command-line parse failed".to_string())
}