mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-19 15:30:16 +00:00
Generalise error & usage writer
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/// Enough context to show full help text.
|
||||||
pub struct HelpWriterContext<'a, ID: 'static> {
|
pub struct HelpWriterContext<'a, ID: 'static> {
|
||||||
pub options: &'a Opts<ID>,
|
pub options: &'a Opts<ID>,
|
||||||
pub program_name: &'a str,
|
pub program_name: &'a str,
|
||||||
@@ -184,3 +185,43 @@ impl<ID> core::fmt::Display for StandardFullHelpWriter<'_, ID> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Enough context to show usage and error information.
|
||||||
|
pub struct ErrorUsageWriterContext<'a, ID: 'static> {
|
||||||
|
pub options: &'a Opts<ID>,
|
||||||
|
pub program_name: &'a str,
|
||||||
|
pub error: ParseError<'a>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ErrorUsageWriter<'a, ID: 'static>: core::fmt::Display {
|
||||||
|
fn new(ctx: ErrorUsageWriterContext<'a, ID>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StandardErrorUsageWriter<'a, ID: 'static>(ErrorUsageWriterContext<'a, ID>);
|
||||||
|
|
||||||
|
impl<'a, ID: 'static> ErrorUsageWriter<'a, ID> for StandardErrorUsageWriter<'a, ID> {
|
||||||
|
fn new(ctx: ErrorUsageWriterContext<'a, ID>) -> Self { Self(ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ID> core::fmt::Display for StandardErrorUsageWriter<'_, ID> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
// Write error
|
||||||
|
writeln!(f, "{name}: {error}", name=self.0.program_name, error = self.0.error)?;
|
||||||
|
|
||||||
|
// Write usage
|
||||||
|
writeln!(f, "{}", StandardShortUsageWriter::new(HelpWriterContext {
|
||||||
|
options: self.0.options,
|
||||||
|
program_name: self.0.program_name
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
// Write full help instruction if a
|
||||||
|
if let Some(help_option) = self.0.options.help_option() {
|
||||||
|
writeln!(f, "Run '{name} {help}' to view all available options.",
|
||||||
|
name = self.0.program_name,
|
||||||
|
// Prefer long name, but otherwise any name is fine
|
||||||
|
help = help_option.first_long_name().unwrap_or(help_option.first_name()))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
use crate::alloc::ParseMapResult;
|
use crate::alloc::ParseMapResult;
|
||||||
use crate::{HandlerResult, HelpWriter, HelpWriterContext, Opt, Opts, ParseControl, ParseError, ParseResult, StandardFullHelpWriter, StandardShortUsageWriter};
|
use crate::{ErrorUsageWriter, ErrorUsageWriterContext, HandlerResult, HelpWriter, HelpWriterContext, Opt, Opts, ParseControl, ParseError, ParseResult, StandardErrorUsageWriter, StandardFullHelpWriter};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{env, eprint, eprintln, print};
|
use std::{env, eprint, print};
|
||||||
|
|
||||||
impl<ID: 'static> Opts<ID> {
|
impl<ID: 'static> Opts<ID> {
|
||||||
/// Wrapper around [Opts::parse] that gathers arguments from the command line and prints errors to stderr.
|
/// Wrapper around [Opts::parse] that gathers arguments from the command line and prints errors to stderr.
|
||||||
@@ -19,7 +19,8 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
pub fn parse_easy<'a>(&self, handler: impl FnMut(&str, &ID, &Opt<ID>, &str, &str) -> HandlerResult<'a, ParseControl>
|
pub fn parse_easy<'a>(&self, handler: impl FnMut(&str, &ID, &Opt<ID>, &str, &str) -> HandlerResult<'a, ParseControl>
|
||||||
) -> ParseResult {
|
) -> ParseResult {
|
||||||
let (program_name, argv) = Self::easy_args();
|
let (program_name, argv) = Self::easy_args();
|
||||||
self.parse(&program_name, argv, handler, |name, e| self.easy_error(name, e))
|
self.parse(&program_name, argv, handler,
|
||||||
|
|name, e| self.eprint_usage::<StandardErrorUsageWriter<'_, ID>>(name, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints full help text for the options using the standard full.
|
/// Prints full help text for the options using the standard full.
|
||||||
@@ -45,21 +46,20 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
eprint!("{}", W::new(ctx));
|
eprint!("{}", W::new(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn easy_args<'a>() -> (Rc<str>, env::Args) {
|
/// Print error & usage text to stderr using the provided error & usage writer.
|
||||||
|
///
|
||||||
|
/// Requires `features = ["std"]`.
|
||||||
|
pub fn eprint_usage<'a, W: ErrorUsageWriter<'a, ID>>(&'a self, program_name: &'a str, error: ParseError<'a>) {
|
||||||
|
let ctx = ErrorUsageWriterContext { options: self, program_name, error };
|
||||||
|
eprint!("{}", W::new(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn easy_args() -> (Rc<str>, env::Args) {
|
||||||
let mut argv = env::args();
|
let mut argv = env::args();
|
||||||
let argv0 = argv.next().unwrap();
|
let argv0 = argv.next().unwrap();
|
||||||
let program_name = Path::new(&argv0).file_name().unwrap().to_string_lossy();
|
let program_name = Path::new(&argv0).file_name().unwrap().to_string_lossy();
|
||||||
(program_name.into(), argv)
|
(program_name.into(), argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn easy_error(&self, program_name: &str, err: ParseError) {
|
|
||||||
eprintln!("{program_name}: {err}");
|
|
||||||
self.eprint_help::<StandardShortUsageWriter<'_, ID>>(program_name);
|
|
||||||
if let Some(help_option) = self.help_option() {
|
|
||||||
eprintln!("Run '{program_name} {help}' to view all available options.",
|
|
||||||
help = help_option.first_long_name().unwrap_or(help_option.first_name()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opts<&'static str> {
|
impl Opts<&'static str> {
|
||||||
@@ -71,6 +71,6 @@ impl Opts<&'static str> {
|
|||||||
let (program_name, argv) = Self::easy_args();
|
let (program_name, argv) = Self::easy_args();
|
||||||
self.parse_map(&program_name, argv,
|
self.parse_map(&program_name, argv,
|
||||||
|name| self.print_full_help(name),
|
|name| self.print_full_help(name),
|
||||||
|name, e| self.easy_error(name, e))
|
|name, e| self.eprint_usage::<StandardErrorUsageWriter<'_, &'static str>>(name, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user