mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-19 07:20:18 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44b2addecb | |||
| 75fc27bc58 | |||
| 8d9e8aea89 | |||
| 20f5a0bf10 | |||
| 7165bb9841 | |||
| 8f6f1827ce | |||
| 33af658e93 | |||
| 75e2bde5fb | |||
| 14028ed2c8 | |||
| 23b6402db6 | |||
| 4f1a01f81c | |||
| a6abeff9f2 | |||
| 87e3e5f4e0 | |||
|
|
82ef9cf8d5 | ||
|
03e1953aae
|
|||
| 148a649273 | |||
| b1a464c79c |
@@ -10,8 +10,3 @@ tab_width = 4
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.py]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = tab
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ members = ["jaarg-nostd"]
|
|||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
license = "MIT"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "It can parse your arguments you should use it it's called jaarg"
|
description = "It can parse your arguments you should use it it's called jaarg"
|
||||||
repository = "https://github.com/gay-pizza/jaarg"
|
repository = "https://github.com/gay-pizza/jaarg"
|
||||||
|
homepage = "https://gay.pizza/"
|
||||||
authors = ["a dinosaur", "Gay Pizza Specifications"]
|
authors = ["a dinosaur", "Gay Pizza Specifications"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -57,7 +57,13 @@ println!("{file:?} -> {out:?} (number: {number:?})",
|
|||||||
|
|
||||||
### Changelog ###
|
### Changelog ###
|
||||||
|
|
||||||
<!-- main: -->
|
v0.2.2:
|
||||||
|
* Fixed coerced `ArgumentError` not being rewritten for positional arguments.
|
||||||
|
* Moved top level includes to `pub use`.
|
||||||
|
* Hopefully work around licence & read me texts not being included in crate.
|
||||||
|
|
||||||
|
v0.2.1:
|
||||||
|
* Fixed licence field in `Cargo.toml`.
|
||||||
|
|
||||||
v0.2.0:
|
v0.2.0:
|
||||||
* Change licence from `MIT` to `MIT OR Apache-2.0`.
|
* Change licence from `MIT` to `MIT OR Apache-2.0`.
|
||||||
@@ -87,7 +93,7 @@ Long term:
|
|||||||
* Make use of const traits when they land to improve table setup.
|
* Make use of const traits when they land to improve table setup.
|
||||||
|
|
||||||
### Projects using jaarg (very cool) ###
|
### Projects using jaarg (very cool) ###
|
||||||
<!-- soon... * [Sprout bootloader](https://github.com/edera-dev/sprout) -->
|
* [Sprout bootloader](https://github.com/edera-dev/sprout)
|
||||||
* [lbminfo](https://github.com/ScrelliCopter/colourcyclinginthehousetonight/tree/main/lbminfo)
|
* [lbminfo](https://github.com/ScrelliCopter/colourcyclinginthehousetonight/tree/main/lbminfo)
|
||||||
|
|
||||||
### Licensing ###
|
### Licensing ###
|
||||||
|
|||||||
@@ -37,17 +37,16 @@ extern "C" fn safe_main(args: &[&str]) -> ExitCode {
|
|||||||
// Parse command-line arguments from argv
|
// Parse command-line arguments from argv
|
||||||
match OPTIONS.parse(
|
match OPTIONS.parse(
|
||||||
SimplePathBuf::from(*args.first().unwrap()).basename(),
|
SimplePathBuf::from(*args.first().unwrap()).basename(),
|
||||||
args.iter().skip(1),
|
args.iter().skip(1), |ctx| {
|
||||||
|program_name, id, _opt, _name, arg| {
|
match ctx.id {
|
||||||
match id {
|
|
||||||
Arg::Help => {
|
Arg::Help => {
|
||||||
let ctx = HelpWriterContext { options: &OPTIONS, program_name };
|
let ctx = HelpWriterContext { options: &OPTIONS, program_name: ctx.program_name };
|
||||||
print!("{}", StandardFullHelpWriter::<'_, Arg>::new(ctx));
|
print!("{}", StandardFullHelpWriter::<'_, Arg>::new(ctx));
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
}
|
}
|
||||||
Arg::Number => { number = str::parse(arg)?; }
|
Arg::Number => { number = str::parse(ctx.arg)?; }
|
||||||
Arg::File => { file = arg.into(); }
|
Arg::File => { file = ctx.arg.into(); }
|
||||||
Arg::Out => { out = Some(arg.into()); }
|
Arg::Out => { out = Some(ctx.arg.into()); }
|
||||||
}
|
}
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}, |program_name, error| {
|
}, |program_name, error| {
|
||||||
@@ -57,7 +56,7 @@ extern "C" fn safe_main(args: &[&str]) -> ExitCode {
|
|||||||
) {
|
) {
|
||||||
ParseResult::ContinueSuccess => (),
|
ParseResult::ContinueSuccess => (),
|
||||||
ParseResult::ExitSuccess => { return ExitCode::SUCCESS; }
|
ParseResult::ExitSuccess => { return ExitCode::SUCCESS; }
|
||||||
ParseResult::ExitError => { return ExitCode::FAILURE; }
|
ParseResult::ExitFailure => { return ExitCode::FAILURE; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the result variables
|
// Print the result variables
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ version.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
|
|||||||
1
jaarg/LICENSE.Apache-2.0
Symbolic link
1
jaarg/LICENSE.Apache-2.0
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../LICENSE.Apache-2.0
|
||||||
1
jaarg/LICENSE.MIT
Symbolic link
1
jaarg/LICENSE.MIT
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../LICENSE.MIT
|
||||||
1
jaarg/README.md
Symbolic link
1
jaarg/README.md
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../README.md
|
||||||
@@ -26,21 +26,21 @@ fn main() {
|
|||||||
]).with_description("My simple utility.");
|
]).with_description("My simple utility.");
|
||||||
|
|
||||||
// Parse command-line arguments from `std::env::args()`
|
// Parse command-line arguments from `std::env::args()`
|
||||||
match OPTIONS.parse_easy(|program_name, id, _opt, _name, arg| {
|
match OPTIONS.parse_easy(|ctx| {
|
||||||
match id {
|
match ctx.id {
|
||||||
Arg::Help => {
|
Arg::Help => {
|
||||||
OPTIONS.print_full_help(program_name);
|
OPTIONS.print_full_help(ctx.program_name);
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
}
|
}
|
||||||
Arg::Number => { number = str::parse(arg)?; }
|
Arg::Number => { number = str::parse(ctx.arg)?; }
|
||||||
Arg::File => { file = arg.into(); }
|
Arg::File => { file = ctx.arg.into(); }
|
||||||
Arg::Out => { out = Some(arg.into()); }
|
Arg::Out => { out = Some(ctx.arg.into()); }
|
||||||
}
|
}
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}) {
|
}) {
|
||||||
ParseResult::ContinueSuccess => (),
|
ParseResult::ContinueSuccess => (),
|
||||||
ParseResult::ExitSuccess => std::process::exit(0),
|
ParseResult::ExitSuccess => std::process::exit(0),
|
||||||
ParseResult::ExitError => std::process::exit(1),
|
ParseResult::ExitFailure => std::process::exit(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the result variables
|
// Print the result variables
|
||||||
|
|||||||
@@ -210,14 +210,14 @@ pub fn main() -> ExitCode {
|
|||||||
Opt::value(Arg::Whitespace, &["--whitespace"], "\" \"").help_text("Emitted indentation (Default: \"\\t\")"),
|
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\
|
]).with_description("Convert one or more binary and text file(s) to a C header file,\n\
|
||||||
as arrays and C strings respectively.");
|
as arrays and C strings respectively.");
|
||||||
match OPTIONS.parse_easy(|program_name, id, _opt, _name, arg| {
|
match OPTIONS.parse_easy(|ctx| {
|
||||||
match id {
|
match ctx.id {
|
||||||
Arg::Out => { arguments.out = arg.into(); }
|
Arg::Out => { arguments.out = ctx.arg.into(); }
|
||||||
Arg::Bin => { jobs.push(Job { job_type: JobType::Binary, path: 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: arg.into() }); }
|
Arg::Txt => { jobs.push(Job { job_type: JobType::Text, path: ctx.arg.into() }); }
|
||||||
Arg::Whitespace => { arguments.whitespace = arg.into(); }
|
Arg::Whitespace => { arguments.whitespace = ctx.arg.into(); }
|
||||||
Arg::Help => {
|
Arg::Help => {
|
||||||
OPTIONS.print_full_help(program_name);
|
OPTIONS.print_full_help(ctx.program_name);
|
||||||
return Ok(ParseControl::Quit);
|
return Ok(ParseControl::Quit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,6 +234,6 @@ pub fn main() -> ExitCode {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ParseResult::ExitSuccess => { ExitCode::SUCCESS }
|
ParseResult::ExitSuccess => { ExitCode::SUCCESS }
|
||||||
ParseResult::ExitError => { ExitCode::FAILURE }
|
ParseResult::ExitFailure => { ExitCode::FAILURE }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,18 @@ impl Opts<&'static str> {
|
|||||||
help: impl Fn(&str), error: impl FnOnce(&str, ParseError)
|
help: impl Fn(&str), error: impl FnOnce(&str, ParseError)
|
||||||
) -> ParseMapResult {
|
) -> ParseMapResult {
|
||||||
let mut out: BTreeMap<&'static str, String> = BTreeMap::new();
|
let mut out: BTreeMap<&'static str, String> = BTreeMap::new();
|
||||||
match self.parse(&program_name, args, |_program_name, id, opt, _name, arg| {
|
match self.parse(program_name, args, |ctx| {
|
||||||
if opt.is_help() {
|
if ctx.option.is_help() {
|
||||||
help(program_name);
|
help(program_name);
|
||||||
Ok(ParseControl::Quit)
|
Ok(ParseControl::Quit)
|
||||||
} else {
|
} else {
|
||||||
out.insert(id, arg.into());
|
out.insert(ctx.id, ctx.arg.into());
|
||||||
Ok(ParseControl::Continue)
|
Ok(ParseControl::Continue)
|
||||||
}
|
}
|
||||||
}, error) {
|
}, error) {
|
||||||
ParseResult::ContinueSuccess => ParseMapResult::Map(out),
|
ParseResult::ContinueSuccess => ParseMapResult::Map(out),
|
||||||
ParseResult::ExitSuccess => ParseMapResult::ExitSuccess,
|
ParseResult::ExitSuccess => ParseMapResult::ExitSuccess,
|
||||||
ParseResult::ExitError => ParseMapResult::ExitFailure,
|
ParseResult::ExitFailure => ParseMapResult::ExitFailure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
* SPDX-License-Identifier: MIT OR Apache-2.0
|
* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::{Opt, Opts};
|
||||||
|
use crate::option::OptType;
|
||||||
|
use crate::options::RequiredParamsBitSet;
|
||||||
|
|
||||||
/// Enum describing the result of parsing arguments, and how the program should behave.
|
/// Enum describing the result of parsing arguments, and how the program should behave.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseResult {
|
pub enum ParseResult {
|
||||||
@@ -11,7 +15,7 @@ pub enum ParseResult {
|
|||||||
/// Parsing succeeded and program should exit with success (eg; `exit(0)`).
|
/// Parsing succeeded and program should exit with success (eg; `exit(0)`).
|
||||||
ExitSuccess,
|
ExitSuccess,
|
||||||
/// There was an error while parsing and program should exit with failure (eg; `exit(1)`).
|
/// There was an error while parsing and program should exit with failure (eg; `exit(1)`).
|
||||||
ExitError,
|
ExitFailure,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution control for parser handlers.
|
/// Execution control for parser handlers.
|
||||||
@@ -24,8 +28,23 @@ pub enum ParseControl {
|
|||||||
Quit,
|
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.
|
/// Result type used by the handler passed to the parser.
|
||||||
type HandlerResult<'a, T> = core::result::Result<T, ParseError<'a>>;
|
pub(crate) type HandlerResult<'a, T> = core::result::Result<T, ParseError<'a>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseError<'a> {
|
pub enum ParseError<'a> {
|
||||||
@@ -114,7 +133,7 @@ impl<ID> Default for ParserState<ID> {
|
|||||||
impl<ID: 'static> Opts<ID> {
|
impl<ID: 'static> Opts<ID> {
|
||||||
/// Parses an iterator of strings as argument tokens.
|
/// 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,
|
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),
|
error: impl FnOnce(&str, ParseError),
|
||||||
) -> ParseResult {
|
) -> ParseResult {
|
||||||
let mut state = ParserState::default();
|
let mut state = ParserState::default();
|
||||||
@@ -127,7 +146,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Call the error handler
|
// Call the error handler
|
||||||
error(program_name, err);
|
error(program_name, err);
|
||||||
return ParseResult::ExitError;
|
return ParseResult::ExitFailure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +154,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
// 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));
|
||||||
return ParseResult::ExitError;
|
return ParseResult::ExitFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that all required arguments have been provided
|
// Ensure that all required arguments have been provided
|
||||||
@@ -144,12 +163,12 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
match option.r#type {
|
match option.r#type {
|
||||||
OptType::Positional => if i >= state.positional_index && option.is_required() {
|
OptType::Positional => if i >= state.positional_index && option.is_required() {
|
||||||
error(program_name, ParseError::RequiredPositional(option.first_name()));
|
error(program_name, ParseError::RequiredPositional(option.first_name()));
|
||||||
return ParseResult::ExitError;
|
return ParseResult::ExitFailure;
|
||||||
}
|
}
|
||||||
OptType::Flag | OptType::Value => if option.is_required() {
|
OptType::Flag | OptType::Value => if option.is_required() {
|
||||||
if !state.required_param_presences.get(required_flag_idx) {
|
if !state.required_param_presences.get(required_flag_idx) {
|
||||||
error(program_name, ParseError::RequiredParameter(option.first_name()));
|
error(program_name, ParseError::RequiredParameter(option.first_name()));
|
||||||
return ParseResult::ExitError;
|
return ParseResult::ExitFailure;
|
||||||
}
|
}
|
||||||
required_flag_idx += 1;
|
required_flag_idx += 1;
|
||||||
}
|
}
|
||||||
@@ -162,10 +181,10 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
|
|
||||||
/// Parse the next token in the argument stream
|
/// Parse the next token in the argument stream
|
||||||
fn next<'a, 'b>(&self, state: &mut ParserState<ID>, token: &'b str, program_name: &str,
|
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 {
|
) -> HandlerResult<'b, ParseControl> where 'a: 'b {
|
||||||
let mut call_handler = |option: &Opt<ID>, name, value| {
|
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
|
// 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))
|
||||||
@@ -228,7 +247,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) {
|
||||||
handler(program_name, &option.id, option, option.first_name(), token)?;
|
call_handler(option, option.first_name(), token)?;
|
||||||
state.positional_index += i + 1;
|
state.positional_index += i + 1;
|
||||||
return Ok(ParseControl::Continue);
|
return Ok(ParseControl::Continue);
|
||||||
}
|
}
|
||||||
@@ -238,3 +257,48 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
extern crate alloc;
|
||||||
|
use alloc::string::String;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
enum ArgID { One, Two, Three, Four, Five }
|
||||||
|
const OPTIONS: Opts<ArgID> = Opts::new(&[
|
||||||
|
Opt::positional(ArgID::One, "one"),
|
||||||
|
Opt::flag(ArgID::Two, &["--two"]),
|
||||||
|
Opt::value(ArgID::Three, &["--three"], "value"),
|
||||||
|
Opt::value(ArgID::Four, &["--four"], "value"),
|
||||||
|
Opt::value(ArgID::Five, &["--five"], "value"),
|
||||||
|
]);
|
||||||
|
const ARGUMENTS: &[&str] = &["one", "--two", "--three=three", "--five=", "--four", "four"];
|
||||||
|
|
||||||
|
//TODO: currently needs alloc to deal with arguments not being able to escape handler
|
||||||
|
let mut one: Option<String> = None;
|
||||||
|
let mut two = false;
|
||||||
|
let mut three: Option<String> = None;
|
||||||
|
let mut four: Option<String> = None;
|
||||||
|
let mut five: Option<String> = None;
|
||||||
|
assert!(matches!(OPTIONS.parse("", ARGUMENTS.iter(), |ctx| {
|
||||||
|
match ctx.id {
|
||||||
|
ArgID::One => { one = Some(ctx.arg.into()); }
|
||||||
|
ArgID::Two => { two = true; }
|
||||||
|
ArgID::Three => { three = Some(ctx.arg.into()); }
|
||||||
|
ArgID::Four => { four = Some(ctx.arg.into()); }
|
||||||
|
ArgID::Five => { five = Some(ctx.arg.into()); }
|
||||||
|
}
|
||||||
|
Ok(ParseControl::Continue)
|
||||||
|
}, |_, error| {
|
||||||
|
panic!("unreachable: {error:?}");
|
||||||
|
}), ParseResult::ContinueSuccess));
|
||||||
|
|
||||||
|
assert_eq!(one, Some("one".into()));
|
||||||
|
assert!(two);
|
||||||
|
assert_eq!(three, Some("three".into()));
|
||||||
|
assert_eq!(four, Some("four".into()));
|
||||||
|
assert_eq!(five, Some("".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* SPDX-License-Identifier: MIT OR Apache-2.0
|
* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::{Opt, Opts, ParseError};
|
||||||
|
use crate::option::{OptIdentifier, OptType};
|
||||||
|
|
||||||
/// Enough context to show full help text.
|
/// 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>,
|
||||||
@@ -166,7 +169,7 @@ impl<ID> core::fmt::Display for StandardFullHelpWriter<'_, ID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write line for option, with aligned help text if needed
|
// Write line for option, with aligned help text if needed
|
||||||
let line = OptionUsageLine(&option);
|
let line = OptionUsageLine(option);
|
||||||
if let Some(help_text) = option.help_string {
|
if let Some(help_text) = option.help_string {
|
||||||
write!(f, " {line:.<align_width$} {help_text}")?;
|
write!(f, " {line:.<align_width$} {help_text}")?;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,10 +8,15 @@
|
|||||||
mod const_utf8;
|
mod const_utf8;
|
||||||
mod ordered_bitset;
|
mod ordered_bitset;
|
||||||
|
|
||||||
include!("option.rs");
|
mod option;
|
||||||
include!("options.rs");
|
mod options;
|
||||||
include!("argparse.rs");
|
mod argparse;
|
||||||
include!("help.rs");
|
mod help;
|
||||||
|
|
||||||
|
pub use option::*;
|
||||||
|
pub use options::*;
|
||||||
|
pub use argparse::*;
|
||||||
|
pub use help::*;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod alloc;
|
pub mod alloc;
|
||||||
|
|||||||
@@ -3,15 +3,17 @@
|
|||||||
* SPDX-License-Identifier: MIT OR Apache-2.0
|
* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::const_utf8;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
enum OptType {
|
pub(crate) enum OptType {
|
||||||
Positional,
|
Positional,
|
||||||
Flag,
|
Flag,
|
||||||
Value,
|
Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum OptIdentifier {
|
pub(crate) enum OptIdentifier {
|
||||||
Single(&'static str),
|
Single(&'static str),
|
||||||
Multi(&'static[&'static str]),
|
Multi(&'static[&'static str]),
|
||||||
}
|
}
|
||||||
@@ -19,11 +21,11 @@ enum OptIdentifier {
|
|||||||
/// Represents an option argument or positional argument to be parsed.
|
/// Represents an option argument or positional argument to be parsed.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Opt<ID> {
|
pub struct Opt<ID> {
|
||||||
id: ID,
|
pub(crate) id: ID,
|
||||||
names: OptIdentifier,
|
pub(crate) names: OptIdentifier,
|
||||||
value_name: Option<&'static str>,
|
pub(crate) value_name: Option<&'static str>,
|
||||||
help_string: Option<&'static str>,
|
pub(crate) help_string: Option<&'static str>,
|
||||||
r#type: OptType,
|
pub(crate) r#type: OptType,
|
||||||
flags: OptFlag,
|
flags: OptFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +124,12 @@ impl<ID> Opt<ID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
const fn is_short_visible(&self) -> bool {
|
pub(crate) const fn is_short_visible(&self) -> bool {
|
||||||
(self.flags.0 & OptFlag::VISIBLE_SHORT.0) != 0
|
(self.flags.0 & OptFlag::VISIBLE_SHORT.0) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
const fn is_full_visible(&self) -> bool {
|
pub(crate) const fn is_full_visible(&self) -> bool {
|
||||||
(self.flags.0 & OptFlag::VISIBLE_FULL.0) != 0
|
(self.flags.0 & OptFlag::VISIBLE_FULL.0) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +163,7 @@ impl<ID: 'static> Opt<ID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the first short option name, if one exists.
|
/// Get the first short option name, if one exists.
|
||||||
const fn first_short_name(&self) -> Option<&'static str> {
|
pub(crate) const fn first_short_name(&self) -> Option<&'static str> {
|
||||||
const fn predicate(name: &str) -> bool {
|
const fn predicate(name: &str) -> bool {
|
||||||
let mut chars = const_utf8::CharIterator::from(name);
|
let mut chars = const_utf8::CharIterator::from(name);
|
||||||
if let Some(first) = chars.next() {
|
if let Some(first) = chars.next() {
|
||||||
@@ -174,7 +176,7 @@ impl<ID: 'static> Opt<ID> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
match self.names {
|
match self.names {
|
||||||
OptIdentifier::Single(name) => if predicate(&name) { Some(name) } else { None },
|
OptIdentifier::Single(name) => if predicate(name) { Some(name) } else { None },
|
||||||
// Can be replaced with `find_map` once iterators are const fn
|
// Can be replaced with `find_map` once iterators are const fn
|
||||||
OptIdentifier::Multi(names) => {
|
OptIdentifier::Multi(names) => {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -190,7 +192,7 @@ impl<ID: 'static> Opt<ID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the first applicable short option's flag character, if one exists.
|
/// Get the first applicable short option's flag character, if one exists.
|
||||||
const fn first_short_name_char(&self) -> Option<char> {
|
pub(crate) const fn first_short_name_char(&self) -> Option<char> {
|
||||||
const fn predicate(name: &str) -> Option<char> {
|
const fn predicate(name: &str) -> Option<char> {
|
||||||
let mut chars = const_utf8::CharIterator::from(name);
|
let mut chars = const_utf8::CharIterator::from(name);
|
||||||
if let Some(first) = chars.next() {
|
if let Some(first) = chars.next() {
|
||||||
@@ -203,7 +205,7 @@ impl<ID: 'static> Opt<ID> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
match self.names {
|
match self.names {
|
||||||
OptIdentifier::Single(name) => predicate(&name),
|
OptIdentifier::Single(name) => predicate(name),
|
||||||
// Can be replaced with `find_map` once iterators are const fn.
|
// Can be replaced with `find_map` once iterators are const fn.
|
||||||
OptIdentifier::Multi(names) => {
|
OptIdentifier::Multi(names) => {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -219,7 +221,7 @@ impl<ID: 'static> Opt<ID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a matching name in the option, offset allows to skip the first `n = offset` characters in the comparison.
|
/// Search for a matching name in the option, offset allows to skip the first `n = offset` characters in the comparison.
|
||||||
fn match_name(&self, string: &str, offset: usize) -> Option<&'static str> {
|
pub(crate) fn match_name(&self, string: &str, offset: usize) -> Option<&'static str> {
|
||||||
let rhs = &string[offset..];
|
let rhs = &string[offset..];
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -239,7 +241,7 @@ impl core::ops::BitOr for OptFlag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod opt_tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -3,18 +3,21 @@
|
|||||||
* SPDX-License-Identifier: MIT OR Apache-2.0
|
* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::{ordered_bitset, Opt};
|
||||||
|
use crate::option::OptType;
|
||||||
|
|
||||||
/// Static structure that contains instructions for parsing command-line arguments.
|
/// Static structure that contains instructions for parsing command-line arguments.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Opts<ID: 'static> {
|
pub struct Opts<ID: 'static> {
|
||||||
/// List of options
|
/// List of options
|
||||||
options: &'static[Opt<ID>],
|
pub(crate) options: &'static[Opt<ID>],
|
||||||
/// String containing single characters that match option prefixes
|
/// String containing single characters that match option prefixes
|
||||||
flag_chars: &'static str,
|
pub(crate) flag_chars: &'static str,
|
||||||
/// A description of what the program does
|
/// A description of what the program does
|
||||||
description: Option<&'static str>,
|
pub(crate) description: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequiredParamsBitSet = ordered_bitset::OrderedBitSet<u32, 4>;
|
pub(crate) type RequiredParamsBitSet = ordered_bitset::OrderedBitSet<u32, 4>;
|
||||||
|
|
||||||
/// The maximum amount of allowed required non-positional options.
|
/// The maximum amount of allowed required non-positional options.
|
||||||
pub const MAX_REQUIRED_OPTIONS: usize = RequiredParamsBitSet::CAPACITY;
|
pub const MAX_REQUIRED_OPTIONS: usize = RequiredParamsBitSet::CAPACITY;
|
||||||
@@ -76,7 +79,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod opts_tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ErrorUsageWriter, ErrorUsageWriterContext, HandlerResult, HelpWriter, HelpWriterContext, Opt, Opts,
|
alloc::ParseMapResult, ErrorUsageWriter, ErrorUsageWriterContext, HandlerResult, HelpWriter, HelpWriterContext,
|
||||||
ParseControl, ParseError, ParseResult, StandardErrorUsageWriter, StandardFullHelpWriter, alloc::ParseMapResult
|
Opts, ParseControl, ParseError, ParseHandlerContext, ParseResult, StandardErrorUsageWriter, StandardFullHelpWriter
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -18,7 +18,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
/// The errors are formatted in a standard user-friendly format.
|
/// The errors are formatted in a standard user-friendly format.
|
||||||
///
|
///
|
||||||
/// Requires `features = ["std"]`.
|
/// 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 {
|
) -> ParseResult {
|
||||||
let (program_name, argv) = Self::easy_args();
|
let (program_name, argv) = Self::easy_args();
|
||||||
self.parse(&program_name, argv, handler,
|
self.parse(&program_name, argv, handler,
|
||||||
|
|||||||
Reference in New Issue
Block a user