Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions argh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@
//! }
//! ```
//!
//! Positional arguments accept values starting with a dash, such as negative numbers
//! (e.g., `-5`) or dash-prefixed filenames. To explicitly mark the end of options,
//! use `--` as a separator.
//! The last positional argument may include a default, or be wrapped in
//! `Option` or `Vec` to indicate an optional or repeating positional argument.
//!
Expand Down Expand Up @@ -1024,12 +1027,16 @@ pub fn parse_struct_args(
continue;
}

if help {
return Err("Trailing arguments are not allowed after `help`.".to_string().into());
}
let is_valid_option = parse_options.arg_to_slot.iter().any(|&(name, _)| name == next_arg);
if is_valid_option {
if help {
return Err("Trailing arguments are not allowed after `help`.".to_string().into());
}

parse_options.parse(next_arg, &mut remaining_args)?;
continue;
parse_options.parse(next_arg, &mut remaining_args)?;
continue;
}
// If it's not a valid option, fall through to parse as positional
}

if let Some(ref mut parse_subcommand) = parse_subcommand {
Expand Down
112 changes: 112 additions & 0 deletions argh/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,118 @@ Required options not provided:
"###,
);
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct NegativeNumber {
#[argh(positional)]
/// a number
n: i32,
}

#[test]
fn negative_number_positional() {
assert_output(&["-5"], NegativeNumber { n: -5 });
assert_output(&["-42"], NegativeNumber { n: -42 });
assert_output(&["5"], NegativeNumber { n: 5 });
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct DashPrefixedString {
#[argh(positional)]
/// a string
s: String,
}

#[test]
fn dash_prefixed_string_positional() {
assert_output(&["-filename"], DashPrefixedString { s: "-filename".into() });
assert_output(&["--weird-name"], DashPrefixedString { s: "--weird-name".into() });
assert_output(&["-"], DashPrefixedString { s: "-".into() });
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct NegativeWithOption {
#[argh(positional)]
/// a number
n: i32,
#[argh(option)]
/// an option
opt: Option<String>,
}

#[test]
fn negative_number_with_option() {
assert_output(&["-5"], NegativeWithOption { n: -5, opt: None });
assert_output(&["-5", "--opt", "value"], NegativeWithOption { n: -5, opt: Some("value".into()) });
assert_output(&["--opt", "value", "-10"], NegativeWithOption { n: -10, opt: Some("value".into()) });
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct NegativeFloatNumber {
#[argh(positional)]
/// a float number
f: f64,
}

#[test]
fn negative_float_positional() {
assert_output(&["-5.5"], NegativeFloatNumber { f: -5.5 });
assert_output(&["-42.1"], NegativeFloatNumber { f: -42.1 });
assert_output(&["5.5"], NegativeFloatNumber { f: 5.5 });
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct WithOptionAndPositional {
#[argh(option)]
/// an option
opt: Option<String>,
#[argh(positional)]
/// a positional
pos: String,
}

#[derive(FromArgs, Debug, PartialEq)]
/// Woot
struct OnlyOption {
#[argh(option)]
/// an option
opt: Option<String>,
}

#[test]
fn unrecognized_option_error() {
// When there are no positional arguments, unrecognized options should error
// Note: error message includes quotes when fuzzy_search is available
#[cfg(not(feature = "fuzzy_search"))]
{
assert_error::<OnlyOption>(
&["--unknown"],
r###"Unrecognized argument: --unknown
"###,
);
}
#[cfg(feature = "fuzzy_search")]
{
assert_error::<OnlyOption>(
&["--unknown"],
r###"Unrecognized argument: "--unknown"
"###,
);
}
}

#[test]
fn negative_number_not_mistaken_for_option() {
assert_output(&["-5"], WithOptionAndPositional { opt: None, pos: "-5".into() });
assert_output(&["-100", "--opt", "val"], WithOptionAndPositional { opt: Some("val".into()), pos: "-100".into() });
// Dash-prefixed strings are also accepted as positional arguments
assert_output(&["--unknown"], WithOptionAndPositional { opt: None, pos: "--unknown".into() });
}
}

/// Tests derived from
Expand Down