Skip to content

Commit

Permalink
Handle option like value as option (#61)
Browse files Browse the repository at this point in the history
From this patch, thwack handles an option like value as **option**.

For example, these arguments are invalid:

```console
thwack --exec --help                # Error
thwack --exec --version             # Error
thwack --starting-point --exec=open # Error
```

However, an option suffixed with `=` still accepts the value as is.

```console
thwack --exec=--help                          # OK
thwack --starting-point=--special-directory-- # OK
```

In addition, this patch introduces `--` (double hyphens) to assume the
rest arguments as `query`. If more than one rest arguments are remained,
these arguments are just joined by a whitespace.
  • Loading branch information
yykamei authored Jun 3, 2021
1 parent 554961e commit 65e53cb
Showing 1 changed file with 115 additions and 50 deletions.
165 changes: 115 additions & 50 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub(crate) const HELP: &str = "thwack
Find a file and open it with an arbitrary command.
USAGE:
thwack [OPTIONS] [query]
thwack [OPTIONS] [--] [query]
ARGS:
<query> The name of the file you'd like to find
Expand Down Expand Up @@ -44,6 +44,7 @@ impl<A: Iterator<Item = OsString>> Parser<A> {
while let Some(arg) = self.next() {
let arg = arg?;
match arg.as_ref() {
"--" => self.consume_rest_as_arg()?,
"-h" => self.set_help(true),
"--help" => self.set_help(true),
"-v" => self.set_version(true),
Expand Down Expand Up @@ -88,6 +89,16 @@ impl<A: Iterator<Item = OsString>> Parser<A> {
Some(arg.to_str().map(|s| s.to_string()).ok_or(error))
}

fn consume_rest_as_arg(&mut self) -> Result<()> {
let mut rest: Vec<String> = Vec::with_capacity(16);
while let Some(val) = self.next() {
let val = val?;
rest.push(val);
}
self.parsed_args.query = rest.join(" ");
Ok(())
}

fn set_help(&mut self, value: bool) {
self.parsed_args.help = value;
}
Expand All @@ -110,7 +121,14 @@ impl<A: Iterator<Item = OsString>> Parser<A> {
let val = if let Some(val) = value {
String::from(val)
} else if let Some(val) = self.next() {
val?
let val = val?;
if val.starts_with('-') {
return Err(Error::args(&format!(
"{}\n\n\"{}\" needs a value.",
HELP, option
)));
}
val
} else {
return Err(Error::args(&format!(
"{}\n\n\"{}\" needs a value.",
Expand Down Expand Up @@ -168,6 +186,39 @@ mod tests {
};
}

#[test]
fn parser_with_double_hyphen() {
assert_eq!(
Parser::new(args!["program", "-h", "--", "abc", "def", "ok!"])
.parse()
.unwrap(),
ParsedArgs {
help: true,
query: String::from("abc def ok!"),
..default!()
}
);
assert_eq!(
Parser::new(args!["program", "--starting-point=/tmp", "--", "hey"])
.parse()
.unwrap(),
ParsedArgs {
starting_point: String::from("/tmp"),
query: String::from("hey"),
..default!()
}
);
assert_eq!(
Parser::new(args!["program", "--", "--abc", "--version", "-h=ok!"])
.parse()
.unwrap(),
ParsedArgs {
query: String::from("--abc --version -h=ok!"),
..default!()
}
);
}

#[test]
fn parser_with_help() {
assert_eq!(
Expand Down Expand Up @@ -246,15 +297,6 @@ mod tests {
..default!()
}
);
assert_eq!(
Parser::new(args!["program", "--starting-point=--option-like-value"])
.parse()
.unwrap(),
ParsedArgs {
starting_point: String::from("--option-like-value"),
..default!()
}
);
assert_eq!(
Parser::new(args!["program", "--starting-point==ok="])
.parse()
Expand Down Expand Up @@ -288,21 +330,13 @@ mod tests {
.message,
format!("{}\n\n\"--starting-point\" needs a value.", HELP),
);
// TODO: Enable this assertion later
// assert_eq!(
// Parser::new(args!["program", "--starting-point", "--option-like-value"])
// .parse()
// .unwrap_err().message,
// format!("{}\n\n\"--starting-point\" needs a value.", HELP),
//);
// TODO: Enable this assertion later
// assert_eq!(
// Parser::new(args!["program", "--starting-point", "--df"])
// .parse()
// .unwrap_err()
// .message,
// format!("{}\n\n\"--starting-point\" needs a value.", HELP),
// );
assert_eq!(
Parser::new(args!["program", "--starting-point", "--"])
.parse()
.unwrap_err()
.message,
format!("{}\n\n\"--starting-point\" needs a value.", HELP),
);
assert_eq!(
Parser::new(args!["program", "--starting-point="])
.parse()
Expand All @@ -316,29 +350,44 @@ mod tests {
}

#[test]
fn parser_with_exec() {
fn parser_with_starting_point_disallow_option_like_value() {
assert_eq!(
Parser::new(args!["program", "--exec=bat"]).parse().unwrap(),
Parser::new(args!["program", "--starting-point", "--option-like-value"])
.parse()
.unwrap_err()
.message,
format!("{}\n\n\"--starting-point\" needs a value.", HELP),
);
}

#[test]
fn parser_with_starting_point_allow_option_like_value() {
assert_eq!(
Parser::new(args!["program", "--starting-point=--option-like-value"])
.parse()
.unwrap(),
ParsedArgs {
exec: String::from("bat"),
starting_point: String::from("--option-like-value"),
..default!()
}
);
}

#[test]
fn parser_with_exec() {
assert_eq!(
Parser::new(args!["program", "--exec", "open"])
.parse()
.unwrap(),
Parser::new(args!["program", "--exec=bat"]).parse().unwrap(),
ParsedArgs {
exec: String::from("open"),
exec: String::from("bat"),
..default!()
}
);
assert_eq!(
Parser::new(args!["program", "--exec=--special--"])
Parser::new(args!["program", "--exec", "open"])
.parse()
.unwrap(),
ParsedArgs {
exec: String::from("--special--"),
exec: String::from("open"),
..default!()
}
);
Expand Down Expand Up @@ -377,21 +426,13 @@ mod tests {
.message,
format!("{}\n\n\"--exec\" needs a value.", HELP),
);
// TODO: Enable this assertion later
// assert_eq!(
// Parser::new(args!["program", "--exec", "--option-like-value"])
// .parse()
// .unwrap_err().message,
// format!("{}\n\n\"--exec\" needs a value.", HELP),
//);
// TODO: Enable this assertion later
// assert_eq!(
// Parser::new(args!["program", "--exec", "--df"])
// .parse()
// .unwrap_err()
// .message,
// format!("{}\n\n\"--exec\" needs a value.", HELP),
// );
assert_eq!(
Parser::new(args!["program", "--exec", "--"])
.parse()
.unwrap_err()
.message,
format!("{}\n\n\"--exec\" needs a value.", HELP),
);
assert_eq!(
Parser::new(args!["program", "--exec="])
.parse()
Expand All @@ -404,6 +445,30 @@ mod tests {
);
}

#[test]
fn parser_with_exec_allow_option_like_value() {
assert_eq!(
Parser::new(args!["program", "--exec=--special--"])
.parse()
.unwrap(),
ParsedArgs {
exec: String::from("--special--"),
..default!()
}
);
}

#[test]
fn parser_with_exec_disallow_option_like_value() {
assert_eq!(
Parser::new(args!["program", "--exec", "--option-like-value"])
.parse()
.unwrap_err()
.message,
format!("{}\n\n\"--exec\" needs a value.", HELP),
);
}

#[test]
fn parsed_args_returns_default() {
let exec = if cfg!(windows) {
Expand Down

0 comments on commit 65e53cb

Please sign in to comment.