mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-19 15:30:16 +00:00
Split parse state validation and make parse context arg optional.
This commit is contained in:
@@ -44,9 +44,9 @@ extern "C" fn safe_main(args: &[&str]) -> ExitCode {
|
|||||||
print!("{}", StandardFullHelpWriter::<'_, Arg>::new(ctx));
|
print!("{}", StandardFullHelpWriter::<'_, Arg>::new(ctx));
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
}
|
}
|
||||||
Arg::Number => { number = str::parse(ctx.arg)?; }
|
Arg::Number => { number = str::parse(ctx.arg.unwrap())?; }
|
||||||
Arg::File => { file = ctx.arg.into(); }
|
Arg::File => { file = ctx.arg.unwrap().into(); }
|
||||||
Arg::Out => { out = Some(ctx.arg.into()); }
|
Arg::Out => { out = Some(ctx.arg.unwrap().into()); }
|
||||||
}
|
}
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}, |program_name, error| {
|
}, |program_name, error| {
|
||||||
|
|||||||
@@ -32,15 +32,15 @@ fn main() {
|
|||||||
OPTIONS.print_full_help(ctx.program_name);
|
OPTIONS.print_full_help(ctx.program_name);
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
}
|
}
|
||||||
Arg::Number => { number = str::parse(ctx.arg)?; }
|
Arg::Number => { number = str::parse(ctx.arg.unwrap().as_ref())?; }
|
||||||
Arg::File => { file = ctx.arg.into(); }
|
Arg::File => { file = ctx.arg.unwrap().into(); }
|
||||||
Arg::Out => { out = Some(ctx.arg.into()); }
|
Arg::Out => { out = Some(ctx.arg.unwrap().into()); }
|
||||||
}
|
}
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}) {
|
}) {
|
||||||
ParseResult::ContinueSuccess => (),
|
ParseResult::ContinueSuccess => (),
|
||||||
ParseResult::ExitSuccess => std::process::exit(0),
|
ParseResult::ExitSuccess => std::process::exit(0),
|
||||||
ParseResult::ExitFailure => std::process::exit(1),
|
ParseResult::ExitFailure => std::process::exit(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the result variables
|
// Print the result variables
|
||||||
|
|||||||
@@ -212,10 +212,10 @@ pub fn main() -> ExitCode {
|
|||||||
as arrays and C strings respectively.");
|
as arrays and C strings respectively.");
|
||||||
match OPTIONS.parse_easy(|ctx| {
|
match OPTIONS.parse_easy(|ctx| {
|
||||||
match ctx.id {
|
match ctx.id {
|
||||||
Arg::Out => { arguments.out = ctx.arg.into(); }
|
Arg::Out => { arguments.out = ctx.arg.unwrap().into(); }
|
||||||
Arg::Bin => { jobs.push(Job { job_type: JobType::Binary, path: ctx.arg.into() }); }
|
Arg::Bin => { jobs.push(Job { job_type: JobType::Binary, path: ctx.arg.unwrap().into() }); }
|
||||||
Arg::Txt => { jobs.push(Job { job_type: JobType::Text, path: ctx.arg.into() }); }
|
Arg::Txt => { jobs.push(Job { job_type: JobType::Text, path: ctx.arg.unwrap().into() }); }
|
||||||
Arg::Whitespace => { arguments.whitespace = ctx.arg.into(); }
|
Arg::Whitespace => { arguments.whitespace = ctx.arg.unwrap().into(); }
|
||||||
Arg::Help => {
|
Arg::Help => {
|
||||||
OPTIONS.print_full_help(ctx.program_name);
|
OPTIONS.print_full_help(ctx.program_name);
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::collections::BTreeMap;
|
|
||||||
use alloc::string::String;
|
|
||||||
use crate::{Opts, ParseControl, ParseError, ParseResult};
|
use crate::{Opts, ParseControl, ParseError, ParseResult};
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
|
use alloc::string::{String, ToString};
|
||||||
|
|
||||||
impl Opts<&'static str> {
|
impl Opts<&'static str> {
|
||||||
/// Parse an iterator of strings as arguments and return the results in a [`BTreeMap`].
|
/// Parse an iterator of strings as arguments and return the results in a [`BTreeMap`].
|
||||||
@@ -22,7 +22,7 @@ impl Opts<&'static str> {
|
|||||||
help(program_name);
|
help(program_name);
|
||||||
Ok(ParseControl::Quit)
|
Ok(ParseControl::Quit)
|
||||||
} else {
|
} else {
|
||||||
out.insert(ctx.id, ctx.arg.into());
|
out.insert(ctx.id, ctx.arg.unwrap().to_string());
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}
|
}
|
||||||
}, error) {
|
}, error) {
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ pub struct ParseHandlerContext<'a, ID: 'static> {
|
|||||||
/// The name of the argument parameter that was matched,
|
/// The name of the argument parameter that was matched,
|
||||||
/// for option parameters this is the token supplied by the user.
|
/// for option parameters this is the token supplied by the user.
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
/// The argument provided to positional arguments and value options, else "".
|
/// The argument provided to positional arguments and value options (will always be Some), or None for flags.
|
||||||
pub arg: &'a str,
|
pub arg: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type used by the handler passed to the parser.
|
/// Result type used by the handler passed to the parser.
|
||||||
@@ -151,6 +151,11 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.validate_state(program_name, state, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_state(&self, program_name: &str, mut state: ParserState<ID>, error: impl FnOnce(&str, ParseError)
|
||||||
|
) -> ParseResult {
|
||||||
// Ensure that value options are provided a value
|
// Ensure that value options are provided a value
|
||||||
if let Some((name, _)) = state.expects_arg.take() {
|
if let Some((name, _)) = state.expects_arg.take() {
|
||||||
error(program_name, ParseError::ExpectArgument(name));
|
error(program_name, ParseError::ExpectArgument(name));
|
||||||
@@ -188,7 +193,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
// HACK: Ensure the string fields are set properly, because coerced
|
// HACK: Ensure the string fields are set properly, because coerced
|
||||||
// ParseIntError/ParseFloatError will have the string fields blanked.
|
// ParseIntError/ParseFloatError will have the string fields blanked.
|
||||||
Err(ParseError::ArgumentError("", "", kind))
|
Err(ParseError::ArgumentError("", "", kind))
|
||||||
=> Err(ParseError::ArgumentError(name, value, kind)),
|
=> Err(ParseError::ArgumentError(name, value.unwrap(), kind)),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
Ok(ctl) => Ok(ctl),
|
Ok(ctl) => Ok(ctl),
|
||||||
}
|
}
|
||||||
@@ -198,7 +203,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
// was matched and didn't have an equals sign separating a value,
|
// was matched and didn't have an equals sign separating a value,
|
||||||
// then call the handler here.
|
// then call the handler here.
|
||||||
if let Some((name, option)) = state.expects_arg.take() {
|
if let Some((name, option)) = state.expects_arg.take() {
|
||||||
call_handler(option, name, token)
|
call_handler(option, name, Some(token))
|
||||||
} else {
|
} else {
|
||||||
// Check if the next argument token starts with an option flag
|
// Check if the next argument token starts with an option flag
|
||||||
if self.flag_chars.chars().any(|c| token.starts_with(c)) {
|
if self.flag_chars.chars().any(|c| token.starts_with(c)) {
|
||||||
@@ -230,9 +235,9 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
|
|
||||||
match (&option.r#type, value_str) {
|
match (&option.r#type, value_str) {
|
||||||
// Call handler for flag-only options
|
// Call handler for flag-only options
|
||||||
(OptType::Flag, None) => call_handler(option, name, ""),
|
(OptType::Flag, None) => call_handler(option, name, None),
|
||||||
// Value was provided this token, so call the handler right now
|
// Value was provided this token, so call the handler right now
|
||||||
(OptType::Value, Some(value)) => call_handler(option, name, value),
|
(OptType::Value, Some(value)) => call_handler(option, name, Some(value)),
|
||||||
// No value available in this token, delay handling to next token
|
// No value available in this token, delay handling to next token
|
||||||
(OptType::Value, None) => {
|
(OptType::Value, None) => {
|
||||||
state.expects_arg = Some((name, option));
|
state.expects_arg = Some((name, option));
|
||||||
@@ -247,7 +252,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
// Find the next positional argument
|
// Find the next positional argument
|
||||||
for (i, option) in self.options[state.positional_index..].iter().enumerate() {
|
for (i, option) in self.options[state.positional_index..].iter().enumerate() {
|
||||||
if matches!(option.r#type, OptType::Positional) {
|
if matches!(option.r#type, OptType::Positional) {
|
||||||
call_handler(option, option.first_name(), token)?;
|
call_handler(option, option.first_name(), Some(token))?;
|
||||||
state.positional_index += i + 1;
|
state.positional_index += i + 1;
|
||||||
return Ok(ParseControl::Continue);
|
return Ok(ParseControl::Continue);
|
||||||
}
|
}
|
||||||
@@ -260,12 +265,13 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate alloc;
|
|
||||||
use alloc::string::String;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test_parse() {
|
||||||
|
extern crate alloc;
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
enum ArgID { One, Two, Three, Four, Five }
|
enum ArgID { One, Two, Three, Four, Five }
|
||||||
const OPTIONS: Opts<ArgID> = Opts::new(&[
|
const OPTIONS: Opts<ArgID> = Opts::new(&[
|
||||||
Opt::positional(ArgID::One, "one"),
|
Opt::positional(ArgID::One, "one"),
|
||||||
@@ -284,11 +290,11 @@ mod tests {
|
|||||||
let mut five: Option<String> = None;
|
let mut five: Option<String> = None;
|
||||||
assert!(matches!(OPTIONS.parse("", ARGUMENTS.iter(), |ctx| {
|
assert!(matches!(OPTIONS.parse("", ARGUMENTS.iter(), |ctx| {
|
||||||
match ctx.id {
|
match ctx.id {
|
||||||
ArgID::One => { one = Some(ctx.arg.into()); }
|
ArgID::One => { one = Some(ctx.arg.unwrap().into()); }
|
||||||
ArgID::Two => { two = true; }
|
ArgID::Two => { two = true; }
|
||||||
ArgID::Three => { three = Some(ctx.arg.into()); }
|
ArgID::Three => { three = Some(ctx.arg.unwrap().into()); }
|
||||||
ArgID::Four => { four = Some(ctx.arg.into()); }
|
ArgID::Four => { four = Some(ctx.arg.unwrap().into()); }
|
||||||
ArgID::Five => { five = Some(ctx.arg.into()); }
|
ArgID::Five => { five = Some(ctx.arg.unwrap().into()); }
|
||||||
}
|
}
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}, |_, error| {
|
}, |_, error| {
|
||||||
|
|||||||
Reference in New Issue
Block a user