Skip to content

Commit

Permalink
Support passthough args in the rust args parser (#20738)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjyw authored Apr 3, 2024
1 parent c90f798 commit 2e010cd
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 13 deletions.
37 changes: 24 additions & 13 deletions src/rust/engine/options/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,28 +97,30 @@ impl Arg {
#[derive(Debug)]
pub struct Args {
args: Vec<Arg>,
passthrough_args: Option<Vec<String>>,
}

impl Args {
// Create an Args instance with the provided args, which must *not* include the
// argv[0] process name.
pub fn new<I: IntoIterator<Item = String>>(arg_strs: I) -> Self {
let mut args: Vec<Arg> = vec![];
let mut passthrough_args: Option<Vec<String>> = None;
let mut scope = Scope::Global;
for arg_str in arg_strs.into_iter() {
if arg_str.starts_with("--") {
let mut args_iter = arg_strs.into_iter();
while let Some(arg_str) = args_iter.next() {
if arg_str == "--" {
// We've hit the passthrough args delimiter (`--`).
passthrough_args = Some(args_iter.collect::<Vec<String>>());
break;
} else if arg_str.starts_with("--") {
let mut components = arg_str.splitn(2, '=');
let flag = components.next().unwrap();
if flag.is_empty() {
// We've hit the passthrough args delimiter (`--`), so don't look further.
break;
} else {
args.push(Arg {
context: scope.clone(),
flag: flag.to_string(),
value: components.next().map(str::to_string),
});
}
args.push(Arg {
context: scope.clone(),
flag: flag.to_string(),
value: components.next().map(str::to_string),
});
} else if arg_str.starts_with('-') && arg_str.len() >= 2 {
let (flag, mut value) = arg_str.split_at(2);
// We support -ldebug and -l=debug, so strip that extraneous equals sign.
Expand All @@ -139,7 +141,10 @@ impl Args {
}
}

Self { args }
Self {
args,
passthrough_args,
}
}

pub fn argv() -> Self {
Expand All @@ -148,6 +153,12 @@ impl Args {
Self::new(env::args().collect::<Vec<_>>())
}

pub fn get_passthrough_args(&self) -> Option<Vec<&str>> {
self.passthrough_args
.as_ref()
.map(|v| Vec::from_iter(v.iter().map(String::as_str)))
}

fn get_list<T: Parseable>(&self, id: &OptionId) -> Result<Option<Vec<ListEdit<T>>>, String> {
let mut edits = vec![];
for arg in &self.args {
Expand Down
35 changes: 35 additions & 0 deletions src/rust/engine/options/src/args_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fn test_string() {
assert_string("quux", option_id!(["scope"], "quux"));

assert!(args.get_string(&option_id!("dne")).unwrap().is_none());
assert!(args.get_passthrough_args().is_none());
}

#[test]
Expand Down Expand Up @@ -84,6 +85,7 @@ fn test_bool() {
assert_bool(true, option_id!(["scope"], "quuxt"));

assert!(args.get_bool(&option_id!("dne")).unwrap().is_none());
assert!(args.get_passthrough_args().is_none());
assert_eq!(
"Problem parsing -c bool value:\n1:swallow\n ^\nExpected 'true' or 'false' at line 1 column 1".to_owned(),
args.get_bool(&option_id!(-'c', "unladen", "capacity"))
Expand All @@ -109,6 +111,7 @@ fn test_float() {
assert_float(1.137, option_id!("baz", "spam"));

assert!(args.get_float(&option_id!("dne")).unwrap().is_none());
assert!(args.get_passthrough_args().is_none());

assert_eq!(
"Problem parsing --bad float value:\n1:swallow\n ^\n\
Expand Down Expand Up @@ -171,6 +174,7 @@ fn test_string_list() {
);

assert!(args.get_string_list(&option_id!("dne")).unwrap().is_none());
assert!(args.get_passthrough_args().is_none());

let expected_error_msg = "\
Problem parsing --bad string list value:
Expand Down Expand Up @@ -350,3 +354,34 @@ fn test_nonexistent_optional_fromfile() {
let args = Args::new(vec!["--foo=@?/does/not/exist".to_string()]);
assert!(args.get_string(&option_id!("foo")).unwrap().is_none());
}

#[test]
fn test_passthrough_args() {
let args = mk_args([
"-ldebug",
"--foo=bar",
"--",
"--passthrough0",
"passthrough1",
"-p",
]);

let assert_string = |expected: &str, id: OptionId| {
assert_eq!(expected.to_owned(), args.get_string(&id).unwrap().unwrap())
};

assert_string("bar", option_id!("foo"));
assert_string("debug", option_id!(-'l', "level"));

assert_eq!(
Some(vec!["--passthrough0", "passthrough1", "-p",]),
args.get_passthrough_args()
);
}

#[test]
fn test_empty_passthrough_args() {
let args = mk_args(["-ldebug", "--foo=bar", "--"]);

assert_eq!(Some(vec![]), args.get_passthrough_args());
}

0 comments on commit 2e010cd

Please sign in to comment.