mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-18 15:00:17 +00:00
Collect parse handler args into a struct with proper names.
This commit is contained in:
@@ -37,17 +37,16 @@ extern "C" fn safe_main(args: &[&str]) -> ExitCode {
|
||||
// Parse command-line arguments from argv
|
||||
match OPTIONS.parse(
|
||||
SimplePathBuf::from(*args.first().unwrap()).basename(),
|
||||
args.iter().skip(1),
|
||||
|program_name, id, _opt, _name, arg| {
|
||||
match id {
|
||||
args.iter().skip(1), |ctx| {
|
||||
match ctx.id {
|
||||
Arg::Help => {
|
||||
let ctx = HelpWriterContext { options: &OPTIONS, program_name };
|
||||
let ctx = HelpWriterContext { options: &OPTIONS, program_name: ctx.program_name };
|
||||
print!("{}", StandardFullHelpWriter::<'_, Arg>::new(ctx));
|
||||
return Ok(ParseControl::Quit);
|
||||
}
|
||||
Arg::Number => { number = str::parse(arg)?; }
|
||||
Arg::File => { file = arg.into(); }
|
||||
Arg::Out => { out = Some(arg.into()); }
|
||||
Arg::Number => { number = str::parse(ctx.arg)?; }
|
||||
Arg::File => { file = ctx.arg.into(); }
|
||||
Arg::Out => { out = Some(ctx.arg.into()); }
|
||||
}
|
||||
Ok(ParseControl::Continue)
|
||||
}, |program_name, error| {
|
||||
|
||||
@@ -26,15 +26,15 @@ fn main() {
|
||||
]).with_description("My simple utility.");
|
||||
|
||||
// Parse command-line arguments from `std::env::args()`
|
||||
match OPTIONS.parse_easy(|program_name, id, _opt, _name, arg| {
|
||||
match id {
|
||||
match OPTIONS.parse_easy(|ctx| {
|
||||
match ctx.id {
|
||||
Arg::Help => {
|
||||
OPTIONS.print_full_help(program_name);
|
||||
OPTIONS.print_full_help(ctx.program_name);
|
||||
return Ok(ParseControl::Quit);
|
||||
}
|
||||
Arg::Number => { number = str::parse(arg)?; }
|
||||
Arg::File => { file = arg.into(); }
|
||||
Arg::Out => { out = Some(arg.into()); }
|
||||
Arg::Number => { number = str::parse(ctx.arg)?; }
|
||||
Arg::File => { file = ctx.arg.into(); }
|
||||
Arg::Out => { out = Some(ctx.arg.into()); }
|
||||
}
|
||||
Ok(ParseControl::Continue)
|
||||
}) {
|
||||
|
||||
@@ -210,14 +210,14 @@ pub fn main() -> ExitCode {
|
||||
Opt::value(Arg::Whitespace, &["--whitespace"], "\" \"").help_text("Emitted indentation (Default: \"\\t\")"),
|
||||
]).with_description("Convert one or more binary and text file(s) to a C header file,\n\
|
||||
as arrays and C strings respectively.");
|
||||
match OPTIONS.parse_easy(|program_name, id, _opt, _name, arg| {
|
||||
match id {
|
||||
Arg::Out => { arguments.out = arg.into(); }
|
||||
Arg::Bin => { jobs.push(Job { job_type: JobType::Binary, path: arg.into() }); }
|
||||
Arg::Txt => { jobs.push(Job { job_type: JobType::Text, path: arg.into() }); }
|
||||
Arg::Whitespace => { arguments.whitespace = arg.into(); }
|
||||
match OPTIONS.parse_easy(|ctx| {
|
||||
match ctx.id {
|
||||
Arg::Out => { arguments.out = ctx.arg.into(); }
|
||||
Arg::Bin => { jobs.push(Job { job_type: JobType::Binary, path: ctx.arg.into() }); }
|
||||
Arg::Txt => { jobs.push(Job { job_type: JobType::Text, path: ctx.arg.into() }); }
|
||||
Arg::Whitespace => { arguments.whitespace = ctx.arg.into(); }
|
||||
Arg::Help => {
|
||||
OPTIONS.print_full_help(program_name);
|
||||
OPTIONS.print_full_help(ctx.program_name);
|
||||
return Ok(ParseControl::Quit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@ impl Opts<&'static str> {
|
||||
help: impl Fn(&str), error: impl FnOnce(&str, ParseError)
|
||||
) -> ParseMapResult {
|
||||
let mut out: BTreeMap<&'static str, String> = BTreeMap::new();
|
||||
match self.parse(program_name, args, |_program_name, id, opt, _name, arg| {
|
||||
if opt.is_help() {
|
||||
match self.parse(program_name, args, |ctx| {
|
||||
if ctx.option.is_help() {
|
||||
help(program_name);
|
||||
Ok(ParseControl::Quit)
|
||||
} else {
|
||||
out.insert(id, arg.into());
|
||||
out.insert(ctx.id, ctx.arg.into());
|
||||
Ok(ParseControl::Continue)
|
||||
}
|
||||
}, error) {
|
||||
|
||||
@@ -28,6 +28,21 @@ pub enum ParseControl {
|
||||
Quit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseHandlerContext<'a, ID: 'static> {
|
||||
/// Name of the program, for printing statuses to the user.
|
||||
pub program_name: &'a str,
|
||||
/// The generic argument ID that was matched.
|
||||
pub id: &'a ID,
|
||||
/// The option that was matched by the parser.
|
||||
pub option: &'a Opt<ID>,
|
||||
/// The name of the argument parameter that was matched,
|
||||
/// for option parameters this is the token supplied by the user.
|
||||
pub name: &'a str,
|
||||
/// The argument provided to positional arguments and value options, else "".
|
||||
pub arg: &'a str,
|
||||
}
|
||||
|
||||
/// Result type used by the handler passed to the parser.
|
||||
pub(crate) type HandlerResult<'a, T> = core::result::Result<T, ParseError<'a>>;
|
||||
|
||||
@@ -118,7 +133,7 @@ impl<ID> Default for ParserState<ID> {
|
||||
impl<ID: 'static> Opts<ID> {
|
||||
/// Parses an iterator of strings as argument tokens.
|
||||
pub fn parse<'a, S: AsRef<str> + 'a, I: Iterator<Item = S>>(&self, program_name: &str, args: I,
|
||||
mut handler: impl FnMut(&str, &ID, &Opt<ID>, &str, &str) -> HandlerResult<'a, ParseControl>,
|
||||
mut handler: impl FnMut(ParseHandlerContext<ID>) -> HandlerResult<'a, ParseControl>,
|
||||
error: impl FnOnce(&str, ParseError),
|
||||
) -> ParseResult {
|
||||
let mut state = ParserState::default();
|
||||
@@ -166,10 +181,10 @@ impl<ID: 'static> Opts<ID> {
|
||||
|
||||
/// Parse the next token in the argument stream
|
||||
fn next<'a, 'b>(&self, state: &mut ParserState<ID>, token: &'b str, program_name: &str,
|
||||
handler: &mut impl FnMut(&str, &ID, &Opt<ID>, &str, &str) -> HandlerResult<'a, ParseControl>
|
||||
handler: &mut impl FnMut(ParseHandlerContext<ID>) -> HandlerResult<'a, ParseControl>
|
||||
) -> HandlerResult<'b, ParseControl> where 'a: 'b {
|
||||
let mut call_handler = |option: &Opt<ID>, name, value| {
|
||||
match handler(program_name, &option.id, option, name, value) {
|
||||
match handler(ParseHandlerContext{ program_name, id: &option.id, option, name, arg: value }) {
|
||||
// HACK: Ensure the string fields are set properly, because coerced
|
||||
// ParseIntError/ParseFloatError will have the string fields blanked.
|
||||
Err(ParseError::ArgumentError("", "", kind))
|
||||
@@ -232,7 +247,7 @@ impl<ID: 'static> Opts<ID> {
|
||||
// Find the next positional argument
|
||||
for (i, option) in self.options[state.positional_index..].iter().enumerate() {
|
||||
if matches!(option.r#type, OptType::Positional) {
|
||||
handler(program_name, &option.id, option, option.first_name(), token)?;
|
||||
call_handler(option, option.first_name(), token)?;
|
||||
state.positional_index += i + 1;
|
||||
return Ok(ParseControl::Continue);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
extern crate std;
|
||||
|
||||
use crate::{
|
||||
ErrorUsageWriter, ErrorUsageWriterContext, HandlerResult, HelpWriter, HelpWriterContext, Opt, Opts,
|
||||
ParseControl, ParseError, ParseResult, StandardErrorUsageWriter, StandardFullHelpWriter, alloc::ParseMapResult
|
||||
alloc::ParseMapResult, ErrorUsageWriter, ErrorUsageWriterContext, HandlerResult, HelpWriter, HelpWriterContext,
|
||||
Opts, ParseControl, ParseError, ParseHandlerContext, ParseResult, StandardErrorUsageWriter, StandardFullHelpWriter
|
||||
};
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
@@ -18,7 +18,7 @@ impl<ID: 'static> Opts<ID> {
|
||||
/// The errors are formatted in a standard user-friendly format.
|
||||
///
|
||||
/// Requires `features = ["std"]`.
|
||||
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(ParseHandlerContext<ID>) -> HandlerResult<'a, ParseControl>
|
||||
) -> ParseResult {
|
||||
let (program_name, argv) = Self::easy_args();
|
||||
self.parse(&program_name, argv, handler,
|
||||
|
||||
Reference in New Issue
Block a user