Skip to content

Commit

Permalink
Merge #32
Browse files Browse the repository at this point in the history
32: publish 0.3.1 r=matklad a=matklad

bors r+

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
  • Loading branch information
bors[bot] and matklad committed Oct 22, 2022
2 parents b77e98a + a7e8fb2 commit 5b33711
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 31 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

## Unreleased

-

## 3.0.1

- Better align with [fuchsia CLI spec](https://fuchsia.dev/fuchsia-src/development/api/cli#command_line_arguments):

* values can begin with `-`: `--takes-value --i-am-the-value`
* support `--` to delimit positional arguments: `cargo run -- --not-an-option`
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = ["xtask/", "crates/*"]

[workspace.package]
version = "0.3.0" # NB: update the dep!
version = "0.3.1" # NB: update the dep!
license = "MIT OR Apache-2.0"
repository = "https://github.com/matklad/xflags"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
Expand Down
70 changes: 70 additions & 0 deletions crates/xflags-macros/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,73 @@ fn subcommand_flag_inheritance() {
expect!["unexpected flag: `--dir`"],
);
}

#[test]
fn edge_cases() {
check(
subcommands::RustAnalyzer::from_vec,
"server --dir --log",
expect![[r#"
RustAnalyzer {
verbose: 0,
subcommand: Server(
Server {
dir: Some(
"--log",
),
subcommand: Launch(
Launch {
log: false,
},
),
},
),
}
"#]],
);
check(
subcommands::RustAnalyzer::from_vec,
"server --dir -- --log",
expect![[r#"
RustAnalyzer {
verbose: 0,
subcommand: Server(
Server {
dir: Some(
"--",
),
subcommand: Launch(
Launch {
log: true,
},
),
},
),
}
"#]],
);
check(
subcommands::RustAnalyzer::from_vec,
"-- -v server",
expect![[r#"unexpected argument: "-v""#]],
);
check(repeated_pos::RepeatedPos::from_vec, "pos 1 prog -j", expect!["unexpected flag: `-j`"]);
check(
repeated_pos::RepeatedPos::from_vec,
"pos 1 -- prog -j",
expect![[r#"
RepeatedPos {
a: "pos",
b: Some(
1,
),
c: Some(
"prog",
),
rest: [
"-j",
],
}
"#]],
);
}
2 changes: 1 addition & 1 deletion crates/xflags/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ edition.workspace = true


[dependencies]
xflags-macros = { path = "../xflags-macros", version = "=0.3.0" }
xflags-macros = { path = "../xflags-macros", version = "=0.3.1" }
58 changes: 29 additions & 29 deletions crates/xflags/src/rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,41 @@ macro_rules! bail {
}

pub struct Parser {
after_double_dash: bool,
rargs: Vec<OsString>,
}

impl Parser {
pub fn new(mut args: Vec<OsString>) -> Self {
args.reverse();
Self { rargs: args }
Self { after_double_dash: false, rargs: args }
}

pub fn new_from_env() -> Self {
let mut args = std::env::args_os().collect::<Vec<_>>();
args.reverse();
args.pop();
Self { rargs: args }
}

pub fn is_empty(&self) -> bool {
self.rargs.is_empty()
let args = std::env::args_os().collect::<Vec<_>>();
let mut res = Parser::new(args);
let _progn = res.next();
res
}

pub fn peek_flag(&self) -> Option<&str> {
self.rargs.last().and_then(|it| it.to_str()).filter(|it| it.starts_with('-'))
}
pub fn pop_flag(&mut self) -> Option<Result<String, OsString>> {
if self.peek_flag().is_some() {
self.next().map(|it| it.into_string())
} else {
if self.after_double_dash {
self.next().map(Err)
} else {
let arg = self.next()?;
let arg_str = arg.to_str().unwrap_or_default();
if arg_str.starts_with('-') {
if arg_str == "--" {
self.after_double_dash = true;
return self.next().map(Err);
}
Some(arg.into_string())
} else {
Some(Err(arg))
}
}
}

pub fn push_back(&mut self, arg: Result<String, OsString>) {
let arg = match arg {
Ok(it) => it.into(),
Expand All @@ -53,15 +58,12 @@ impl Parser {
self.rargs.push(arg)
}

pub fn next(&mut self) -> Option<OsString> {
fn next(&mut self) -> Option<OsString> {
self.rargs.pop()
}

pub fn next_value(&mut self, flag: &str) -> Result<OsString> {
if self.peek_flag().is_some() {
bail!("expected a value for `{}`", flag)
}
self.next().ok_or_else(|| format_err!("expected a value for `{}`", flag))
self.next().ok_or_else(|| format_err!("expected a value for `{flag}`"))
}

pub fn next_value_from_str<T: FromStr>(&mut self, flag: &str) -> Result<T>
Expand All @@ -77,21 +79,19 @@ impl Parser {
T::Err: fmt::Display,
{
match value.into_string() {
Ok(str) => {
str.parse::<T>().map_err(|err| format_err!("can't parse `{}`, {}", flag, err))
}
Ok(str) => str.parse::<T>().map_err(|err| format_err!("can't parse `{flag}`, {err}")),
Err(it) => {
bail!("can't parse `{}`, invalid utf8: {:?}", flag, it)
bail!("can't parse `{flag}`, invalid utf8: {it:?}")
}
}
}

pub fn unexpected_flag(&self, flag: &str) -> Error {
format_err!("unexpected flag: `{}`", flag)
format_err!("unexpected flag: `{flag}`")
}

pub fn unexpected_arg(&self, arg: OsString) -> Error {
format_err!("unexpected argument: {:?}", arg)
format_err!("unexpected argument: {arg:?}")
}

pub fn subcommand_required(&self) -> Error {
Expand All @@ -104,15 +104,15 @@ impl Parser {

pub fn optional<T>(&self, flag: &str, mut vals: Vec<T>) -> Result<Option<T>> {
if vals.len() > 1 {
bail!("flag specified more than once: `{}`", flag)
bail!("flag specified more than once: `{flag}`")
}
Ok(vals.pop())
}

pub fn required<T>(&self, flag: &str, mut vals: Vec<T>) -> Result<T> {
if vals.len() > 1 {
bail!("flag specified more than once: `{}`", flag)
bail!("flag specified more than once: `{flag}`")
}
vals.pop().ok_or_else(|| format_err!("flag is required: `{}`", flag))
vals.pop().ok_or_else(|| format_err!("flag is required: `{flag}`"))
}
}

0 comments on commit 5b33711

Please sign in to comment.