summaryrefslogtreecommitdiffstats
path: root/vendor/xflags-macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/xflags-macros/src')
-rw-r--r--vendor/xflags-macros/src/ast.rs13
-rw-r--r--vendor/xflags-macros/src/emit.rs350
-rw-r--r--vendor/xflags-macros/src/lib.rs26
-rw-r--r--vendor/xflags-macros/src/parse.rs166
-rw-r--r--vendor/xflags-macros/src/update.rs3
5 files changed, 358 insertions, 200 deletions
diff --git a/vendor/xflags-macros/src/ast.rs b/vendor/xflags-macros/src/ast.rs
index 5ccebd951..7fb9df1e1 100644
--- a/vendor/xflags-macros/src/ast.rs
+++ b/vendor/xflags-macros/src/ast.rs
@@ -4,6 +4,12 @@ pub(crate) struct XFlags {
pub(crate) cmd: Cmd,
}
+impl XFlags {
+ pub fn is_anon(&self) -> bool {
+ self.cmd.name.is_empty()
+ }
+}
+
#[derive(Debug)]
pub(crate) struct Cmd {
pub(crate) name: String,
@@ -12,6 +18,7 @@ pub(crate) struct Cmd {
pub(crate) flags: Vec<Flag>,
pub(crate) subcommands: Vec<Cmd>,
pub(crate) default: bool,
+ pub(crate) idx: u8,
}
#[derive(Debug)]
@@ -30,6 +37,12 @@ pub(crate) struct Flag {
pub(crate) val: Option<Val>,
}
+impl Flag {
+ pub(crate) fn is_help(&self) -> bool {
+ self.name == "help"
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Arity {
Optional,
diff --git a/vendor/xflags-macros/src/emit.rs b/vendor/xflags-macros/src/emit.rs
index 05d5f01dd..1f3d88ead 100644
--- a/vendor/xflags-macros/src/emit.rs
+++ b/vendor/xflags-macros/src/emit.rs
@@ -1,15 +1,25 @@
use crate::{ast, update};
-use std::{fmt::Write, path::Path};
+use std::{env, fmt::Write, path::Path};
+
+macro_rules! w {
+ ($($tt:tt)*) => {
+ drop(write!($($tt)*))
+ };
+}
pub(crate) fn emit(xflags: &ast::XFlags) -> String {
let mut buf = String::new();
+ if xflags.is_anon() {
+ w!(buf, "{{\n");
+ }
+
emit_cmd(&mut buf, &xflags.cmd);
blank_line(&mut buf);
emit_api(&mut buf, xflags);
- if std::env::var("UPDATE_XFLAGS").is_ok() {
+ if !xflags.is_anon() && env::var("UPDATE_XFLAGS").is_ok() {
if let Some(src) = &xflags.src {
update::in_place(&buf, Path::new(src.as_str()))
} else {
@@ -22,22 +32,22 @@ pub(crate) fn emit(xflags: &ast::XFlags) -> String {
}
blank_line(&mut buf);
- emit_impls(&mut buf, &xflags);
+ emit_impls(&mut buf, xflags);
emit_help(&mut buf, xflags);
- buf
-}
+ if xflags.is_anon() {
+ w!(buf, "Flags::from_env_or_exit()");
+ w!(buf, "}}\n");
+ }
-macro_rules! w {
- ($($tt:tt)*) => {
- drop(write!($($tt)*))
- };
+ buf
}
fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) {
w!(buf, "#[derive(Debug)]\n");
w!(buf, "pub struct {}", cmd.ident());
- if cmd.args.is_empty() && cmd.flags.is_empty() && cmd.subcommands.is_empty() {
+ let flags = cmd.flags.iter().filter(|it| !it.is_help()).collect::<Vec<_>>();
+ if cmd.args.is_empty() && flags.is_empty() && cmd.subcommands.is_empty() {
w!(buf, ";\n");
return;
}
@@ -45,16 +55,16 @@ fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) {
for arg in &cmd.args {
let ty = gen_arg_ty(arg.arity, &arg.val.ty);
- w!(buf, " pub {}: {},\n", arg.val.ident(), ty);
+ w!(buf, " pub {}: {ty},\n", arg.val.ident());
}
- if !cmd.args.is_empty() && !cmd.flags.is_empty() {
+ if !cmd.args.is_empty() && !flags.is_empty() {
blank_line(buf);
}
- for flag in &cmd.flags {
+ for flag in &flags {
let ty = gen_flag_ty(flag.arity, flag.val.as_ref().map(|it| &it.ty));
- w!(buf, " pub {}: {},\n", flag.ident(), ty);
+ w!(buf, " pub {}: {ty},\n", flag.ident());
}
if cmd.has_subcommands() {
@@ -67,8 +77,8 @@ fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) {
w!(buf, "#[derive(Debug)]\n");
w!(buf, "pub enum {} {{\n", cmd.cmd_enum_ident());
for sub in &cmd.subcommands {
- let name = camel(&sub.name);
- w!(buf, " {}({}),\n", name, name);
+ let name = sub.ident();
+ w!(buf, " {name}({name}),\n");
}
w!(buf, "}}\n");
@@ -104,9 +114,12 @@ fn gen_arg_ty(arity: ast::Arity, ty: &ast::Ty) -> String {
}
fn emit_api(buf: &mut String, xflags: &ast::XFlags) {
- w!(buf, "impl {} {{\n", camel(&xflags.cmd.name));
+ w!(buf, "impl {} {{\n", xflags.cmd.ident());
- w!(buf, " pub const HELP: &'static str = Self::HELP_;\n");
+ w!(buf, " #[allow(dead_code)]\n");
+ w!(buf, " pub fn from_env_or_exit() -> Self {{\n");
+ w!(buf, " Self::from_env_or_exit_()\n");
+ w!(buf, " }}\n");
blank_line(buf);
w!(buf, " #[allow(dead_code)]\n");
@@ -123,7 +136,10 @@ fn emit_api(buf: &mut String, xflags: &ast::XFlags) {
}
fn emit_impls(buf: &mut String, xflags: &ast::XFlags) -> () {
- w!(buf, "impl {} {{\n", camel(&xflags.cmd.name));
+ w!(buf, "impl {} {{\n", xflags.cmd.ident());
+ w!(buf, " fn from_env_or_exit_() -> Self {{\n");
+ w!(buf, " Self::from_env_().unwrap_or_else(|err| err.exit())\n");
+ w!(buf, " }}\n");
w!(buf, " fn from_env_() -> xflags::Result<Self> {{\n");
w!(buf, " let mut p = xflags::rt::Parser::new_from_env();\n");
w!(buf, " Self::parse_(&mut p)\n");
@@ -134,101 +150,115 @@ fn emit_impls(buf: &mut String, xflags: &ast::XFlags) -> () {
w!(buf, " }}\n");
w!(buf, "}}\n");
blank_line(buf);
- emit_impls_rec(buf, &xflags.cmd)
-}
-
-fn emit_impls_rec(buf: &mut String, cmd: &ast::Cmd) -> () {
- emit_impl(buf, cmd);
- for sub in &cmd.subcommands {
- blank_line(buf);
- emit_impls_rec(buf, sub);
- }
+ emit_parse(buf, &xflags.cmd)
}
-fn emit_impl(buf: &mut String, cmd: &ast::Cmd) -> () {
- w!(buf, "impl {} {{\n", camel(&cmd.name));
+fn emit_parse(buf: &mut String, cmd: &ast::Cmd) {
+ w!(buf, "impl {} {{\n", cmd.ident());
w!(buf, "fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {{\n");
+ w!(buf, "#![allow(non_snake_case)]\n");
- for flag in &cmd.flags {
- w!(buf, "let mut {} = Vec::new();\n", flag.ident());
- }
+ let mut prefix = String::new();
+ emit_locals_rec(buf, &mut prefix, cmd);
blank_line(buf);
+ w!(buf, "let mut state_ = 0u8;\n");
+ w!(buf, "while let Some(arg_) = p_.pop_flag() {{\n");
+ w!(buf, "match arg_ {{\n");
+ {
+ w!(buf, "Ok(flag_) => match (state_, flag_.as_str()) {{\n");
+ emit_match_flag_rec(buf, &mut prefix, cmd);
+ w!(buf, "_ => return Err(p_.unexpected_flag(&flag_)),\n");
+ w!(buf, "}}\n");
- if !cmd.args.is_empty() {
- for arg in &cmd.args {
- w!(buf, "let mut {} = (false, Vec::new());\n", arg.val.ident());
- }
- blank_line(buf);
+ w!(buf, "Err(arg_) => match (state_, arg_.to_str().unwrap_or(\"\")) {{\n");
+ emit_match_arg_rec(buf, &mut prefix, cmd);
+ w!(buf, "_ => return Err(p_.unexpected_arg(arg_)),\n");
+ w!(buf, "}}\n");
}
+ w!(buf, "}}\n");
+ w!(buf, "}}\n");
+ emit_default_transitions(buf, cmd);
- if cmd.has_subcommands() {
- w!(buf, "let mut sub_ = None;");
- blank_line(buf);
+ w!(buf, "Ok(");
+ emit_record_rec(buf, &mut prefix, cmd);
+ w!(buf, ")");
+
+ w!(buf, "}}\n");
+ w!(buf, "}}\n");
+}
+
+fn emit_locals_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) {
+ for flag in &cmd.flags {
+ if !flag.is_help() {
+ w!(buf, "let mut {prefix}{} = Vec::new();\n", flag.ident());
+ }
+ }
+ for arg in &cmd.args {
+ w!(buf, "let mut {prefix}{} = (false, Vec::new());\n", arg.val.ident());
+ }
+ for sub in &cmd.subcommands {
+ let l = sub.push_prefix(prefix);
+ emit_locals_rec(buf, prefix, sub);
+ prefix.truncate(l);
}
+}
- w!(buf, "while let Some(arg_) = p_.pop_flag() {{\n");
- w!(buf, "match arg_ {{\n");
- {
- w!(buf, "Ok(flag_) => match flag_.as_str() {{\n");
- for flag in &cmd.flags {
- w!(buf, "\"--{}\"", flag.name);
- if let Some(short) = &flag.short {
- w!(buf, "| \"-{}\"", short);
- }
- w!(buf, " => {}.push(", flag.ident());
+fn emit_match_flag_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) {
+ for flag in &cmd.flags {
+ w!(buf, "(");
+ emit_all_ids_rec(buf, cmd);
+ w!(buf, ", \"--{}\"", flag.name);
+ if let Some(short) = &flag.short {
+ w!(buf, "| \"-{short}\"");
+ }
+ w!(buf, ") => ");
+ if flag.is_help() {
+ w!(buf, "return Err(p_.help(Self::HELP_)),");
+ } else {
+ w!(buf, "{prefix}{}.push(", flag.ident());
match &flag.val {
Some(val) => match &val.ty {
ast::Ty::OsString | ast::Ty::PathBuf => {
w!(buf, "p_.next_value(&flag_)?.into()")
}
ast::Ty::FromStr(ty) => {
- w!(buf, "p_.next_value_from_str::<{}>(&flag_)?", ty)
+ w!(buf, "p_.next_value_from_str::<{ty}>(&flag_)?")
}
},
None => w!(buf, "()"),
}
w!(buf, "),");
}
- if cmd.default_subcommand().is_some() {
- w!(buf, "_ => {{ p_.push_back(Ok(flag_)); break; }}");
- } else {
- w!(buf, "_ => return Err(p_.unexpected_flag(&flag_)),");
- }
- w!(buf, "}}\n");
}
- {
- w!(buf, "Err(arg_) => {{\n");
- if cmd.has_subcommands() {
- w!(buf, "match arg_.to_str().unwrap_or(\"\") {{\n");
- for sub in cmd.named_subcommands() {
- w!(buf, "\"{}\" => {{\n", sub.name);
- w!(
- buf,
- "sub_ = Some({}::{}({}::parse_(p_)?));",
- cmd.cmd_enum_ident(),
- sub.ident(),
- sub.ident()
- );
- w!(buf, "break;");
- w!(buf, "}}\n");
- }
- w!(buf, "_ => (),\n");
- w!(buf, "}}\n");
- }
+ if let Some(sub) = cmd.default_subcommand() {
+ w!(buf, "({}, _) => {{ p_.push_back(Ok(flag_)); state_ = {}; }}", cmd.idx, sub.idx);
+ }
+ for sub in &cmd.subcommands {
+ let l = sub.push_prefix(prefix);
+ emit_match_flag_rec(buf, prefix, sub);
+ prefix.truncate(l);
+ }
+}
+fn emit_match_arg_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) {
+ for sub in cmd.named_subcommands() {
+ w!(buf, "({}, \"{}\") => state_ = {},\n", cmd.idx, sub.name, sub.idx);
+ }
+ if !cmd.args.is_empty() || cmd.has_subcommands() {
+ w!(buf, "({}, _) => {{\n", cmd.idx);
for arg in &cmd.args {
let done = match arg.arity {
ast::Arity::Optional | ast::Arity::Required => "done_ @ ",
ast::Arity::Repeated => "",
};
- w!(buf, "if let ({}false, buf_) = &mut {} {{\n", done, arg.val.ident());
+ w!(buf, "if let ({done}false, buf_) = &mut {prefix}{} {{\n", arg.val.ident());
w!(buf, "buf_.push(");
match &arg.val.ty {
ast::Ty::OsString | ast::Ty::PathBuf => {
w!(buf, "arg_.into()")
}
ast::Ty::FromStr(ty) => {
- w!(buf, "p_.value_from_str::<{}>(\"{}\", arg_)?", ty, arg.val.name);
+ w!(buf, "p_.value_from_str::<{ty}>(\"{}\", arg_)?", arg.val.name);
}
}
w!(buf, ");\n");
@@ -241,79 +271,110 @@ fn emit_impl(buf: &mut String, cmd: &ast::Cmd) -> () {
w!(buf, "continue;\n");
w!(buf, "}}\n");
}
- if cmd.default_subcommand().is_some() {
- w!(buf, "p_.push_back(Err(arg_)); break;");
+
+ if let Some(sub) = cmd.default_subcommand() {
+ w!(buf, "p_.push_back(Err(arg_)); state_ = {};", sub.idx);
} else {
w!(buf, "return Err(p_.unexpected_arg(arg_));");
}
w!(buf, "}}\n");
}
- w!(buf, "}}\n");
- w!(buf, "}}\n");
- if let Some(sub) = cmd.default_subcommand() {
- w!(buf, "if sub_.is_none() {{\n");
- w!(
- buf,
- "sub_ = Some({}::{}({}::parse_(p_)?));",
- cmd.cmd_enum_ident(),
- sub.ident(),
- sub.ident()
- );
- w!(buf, "}}\n");
+ for sub in &cmd.subcommands {
+ let l = sub.push_prefix(prefix);
+ emit_match_arg_rec(buf, prefix, sub);
+ prefix.truncate(l);
}
+}
- w!(buf, "Ok(Self {{\n");
- if !cmd.args.is_empty() {
- for arg in &cmd.args {
- let val = &arg.val;
- w!(buf, "{}: ", val.ident());
- match arg.arity {
- ast::Arity::Optional => {
- w!(buf, "p_.optional(\"{}\", {}.1)?", val.name, val.ident())
- }
- ast::Arity::Required => {
- w!(buf, "p_.required(\"{}\", {}.1)?", val.name, val.ident())
- }
- ast::Arity::Repeated => w!(buf, "{}.1", val.ident()),
- }
- w!(buf, ",\n");
- }
- blank_line(buf);
- }
+fn emit_record_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) {
+ w!(buf, "{} {{\n", cmd.ident());
for flag in &cmd.flags {
+ if flag.is_help() {
+ continue;
+ }
w!(buf, "{}: ", flag.ident());
match &flag.val {
Some(_val) => match flag.arity {
ast::Arity::Optional => {
- w!(buf, "p_.optional(\"--{}\", {})?", flag.name, flag.ident())
+ w!(buf, "p_.optional(\"--{}\", {prefix}{})?", flag.name, flag.ident())
}
ast::Arity::Required => {
- w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident())
+ w!(buf, "p_.required(\"--{}\", {prefix}{})?", flag.name, flag.ident())
}
- ast::Arity::Repeated => w!(buf, "{}", flag.ident()),
+ ast::Arity::Repeated => w!(buf, "{prefix}{}", flag.ident()),
},
None => match flag.arity {
ast::Arity::Optional => {
- w!(buf, "p_.optional(\"--{}\", {})?.is_some()", flag.name, flag.ident())
+ w!(buf, "p_.optional(\"--{}\", {prefix}{})?.is_some()", flag.name, flag.ident())
}
ast::Arity::Required => {
- w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident())
+ w!(buf, "p_.required(\"--{}\", {prefix}{})?", flag.name, flag.ident())
}
- ast::Arity::Repeated => w!(buf, "{}.len() as u32", flag.ident()),
+ ast::Arity::Repeated => w!(buf, "{prefix}{}.len() as u32", flag.ident()),
},
}
w!(buf, ",\n");
}
+ for arg in &cmd.args {
+ let val = &arg.val;
+ w!(buf, "{}: ", val.ident());
+ match arg.arity {
+ ast::Arity::Optional => {
+ w!(buf, "p_.optional(\"{}\", {prefix}{}.1)?", val.name, val.ident())
+ }
+ ast::Arity::Required => {
+ w!(buf, "p_.required(\"{}\", {prefix}{}.1)?", val.name, val.ident())
+ }
+ ast::Arity::Repeated => w!(buf, "{prefix}{}.1", val.ident()),
+ }
+ w!(buf, ",\n");
+ }
if cmd.has_subcommands() {
- w!(buf, "subcommand: p_.subcommand(sub_)?,\n");
+ w!(buf, "subcommand: match state_ {{\n");
+ for sub in &cmd.subcommands {
+ emit_leaf_ids_rec(buf, sub);
+ w!(buf, " => {}::{}(", cmd.cmd_enum_ident(), sub.ident());
+ let l = prefix.len();
+ prefix.push_str(&snake(&sub.name));
+ prefix.push_str("__");
+ emit_record_rec(buf, prefix, sub);
+ prefix.truncate(l);
+ w!(buf, "),\n");
+ }
+ w!(buf, "_ => return Err(p_.subcommand_required()),");
+ w!(buf, "}}\n");
}
- w!(buf, "}})\n");
- w!(buf, "}}\n");
- w!(buf, "}}\n");
+ w!(buf, "}}");
+}
+
+fn emit_leaf_ids_rec(buf: &mut String, cmd: &ast::Cmd) {
+ if cmd.has_subcommands() {
+ for sub in &cmd.subcommands {
+ emit_leaf_ids_rec(buf, sub)
+ }
+ } else {
+ w!(buf, "| {}", cmd.idx)
+ }
+}
+
+fn emit_all_ids_rec(buf: &mut String, cmd: &ast::Cmd) {
+ w!(buf, "| {}", cmd.idx);
+ for sub in &cmd.subcommands {
+ emit_all_ids_rec(buf, sub)
+ }
+}
+
+fn emit_default_transitions(buf: &mut String, cmd: &ast::Cmd) {
+ if let Some(sub) = cmd.default_subcommand() {
+ w!(buf, "state_ = if state_ == {} {{ {} }} else {{ state_ }};", cmd.idx, sub.idx);
+ }
+ for sub in &cmd.subcommands {
+ emit_default_transitions(buf, sub);
+ }
}
fn emit_help(buf: &mut String, xflags: &ast::XFlags) {
@@ -327,7 +388,7 @@ fn emit_help(buf: &mut String, xflags: &ast::XFlags) {
let help = format!("{:?}", help);
let help = help.replace("\\n", "\n").replacen("\"", "\"\\\n", 1);
- w!(buf, "const HELP_: &'static str = {};", help);
+ w!(buf, "const HELP_: &'static str = {help};");
w!(buf, "}}\n");
}
@@ -336,26 +397,34 @@ fn write_lines_indented(buf: &mut String, multiline_str: &str, indent: usize) {
if line.is_empty() {
w!(buf, "\n")
} else {
- w!(buf, "{blank:indent$}{}\n", line, indent = indent, blank = "");
+ w!(buf, "{blank:indent$}{line}\n", blank = "");
}
}
}
fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) {
- w!(buf, "{}{}\n", prefix, cmd.name);
+ let mut empty_help = true;
+ if !cmd.name.is_empty() {
+ empty_help = false;
+ w!(buf, "{}{}\n", prefix, cmd.name);
+ }
if let Some(doc) = &cmd.doc {
+ empty_help = false;
write_lines_indented(buf, doc, 2);
}
let indent = if prefix.is_empty() { "" } else { " " };
let args = cmd.args_with_default();
if !args.is_empty() {
- blank_line(buf);
+ if !empty_help {
+ blank_line(buf);
+ }
+ empty_help = false;
w!(buf, "{}ARGS:\n", indent);
let mut blank = "";
for arg in &args {
- w!(buf, "{}", blank);
+ w!(buf, "{blank}");
blank = "\n";
let (l, r) = match arg.arity {
@@ -363,7 +432,7 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) {
ast::Arity::Required => ("<", ">"),
ast::Arity::Repeated => ("<", ">..."),
};
- w!(buf, " {}{}{}\n", l, arg.val.name, r);
+ w!(buf, " {l}{}{r}\n", arg.val.name);
if let Some(doc) = &arg.doc {
write_lines_indented(buf, doc, 6)
}
@@ -372,17 +441,19 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) {
let flags = cmd.flags_with_default();
if !flags.is_empty() {
- blank_line(buf);
- w!(buf, "{}OPTIONS:\n", indent);
+ if !empty_help {
+ blank_line(buf);
+ }
+ w!(buf, "{indent}OPTIONS:\n");
let mut blank = "";
for flag in &flags {
- w!(buf, "{}", blank);
+ w!(buf, "{blank}",);
blank = "\n";
- let short = flag.short.as_ref().map(|it| format!("-{}, ", it)).unwrap_or_default();
+ let short = flag.short.as_ref().map(|it| format!("-{it}, ")).unwrap_or_default();
let value = flag.val.as_ref().map(|it| format!(" <{}>", it.name)).unwrap_or_default();
- w!(buf, " {}--{}{}\n", short, flag.name, value);
+ w!(buf, " {short}--{}{value}\n", flag.name);
if let Some(doc) = &flag.doc {
write_lines_indented(buf, doc, 6);
}
@@ -407,11 +478,20 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) {
impl ast::Cmd {
fn ident(&self) -> String {
+ if self.name.is_empty() {
+ return "Flags".to_string();
+ }
camel(&self.name)
}
fn cmd_enum_ident(&self) -> String {
format!("{}Cmd", self.ident())
}
+ fn push_prefix(&self, buf: &mut String) -> usize {
+ let l = buf.len();
+ buf.push_str(&snake(&self.name));
+ buf.push_str("__");
+ l
+ }
fn has_subcommands(&self) -> bool {
!self.subcommands.is_empty()
}
@@ -510,10 +590,10 @@ mod tests {
#[test]
fn gen_it() {
- let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/it");
+ let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
let mut did_update = false;
- for entry in fs::read_dir(test_dir.join("src")).unwrap() {
+ for entry in fs::read_dir(test_dir.join("data")).unwrap() {
let entry = entry.unwrap();
let text = fs::read_to_string(entry.path()).unwrap();
@@ -531,7 +611,7 @@ mod tests {
);
let name = entry.file_name();
- did_update |= update_on_disk_if_different(&test_dir.join(name), code);
+ did_update |= update_on_disk_if_different(&test_dir.join("it").join(name), code);
if fmt.is_none() {
panic!("syntax error");
diff --git a/vendor/xflags-macros/src/lib.rs b/vendor/xflags-macros/src/lib.rs
index 8fac00587..f1c0e0599 100644
--- a/vendor/xflags-macros/src/lib.rs
+++ b/vendor/xflags-macros/src/lib.rs
@@ -8,19 +8,39 @@ pub fn xflags(_ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Stub out the code, but let rust-analyzer resolve the invocation
#[cfg(not(test))]
{
- let cmd = parse::parse(_ts).unwrap();
- let text = emit::emit(&cmd);
+ let text = match parse::xflags(_ts) {
+ Ok(cmd) => emit::emit(&cmd),
+ Err(err) => format!("compile_error!(\"invalid flags syntax, {err}\");"),
+ };
text.parse().unwrap()
}
#[cfg(test)]
unimplemented!()
}
+#[proc_macro]
+pub fn parse_or_exit(_ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ // Stub out the code, but let rust-analyzer resolve the invocation
+ #[cfg(not(test))]
+ {
+ let text = match parse::parse_or_exit(_ts) {
+ Ok(cmd) => emit::emit(&cmd),
+ Err(err) => format!("compile_error!(\"invalid flags syntax, {err}\")"),
+ };
+ text.parse().unwrap()
+ }
+ #[cfg(test)]
+ {
+ let _ = parse::parse_or_exit;
+ unimplemented!();
+ }
+}
+
#[cfg(test)]
pub fn compile(src: &str) -> String {
use proc_macro2::TokenStream;
let ts = src.parse::<TokenStream>().unwrap();
- let cmd = parse::parse(ts).unwrap();
+ let cmd = parse::xflags(ts).unwrap();
emit::emit(&cmd)
}
diff --git a/vendor/xflags-macros/src/parse.rs b/vendor/xflags-macros/src/parse.rs
index e9749b141..48f9588e9 100644
--- a/vendor/xflags-macros/src/parse.rs
+++ b/vendor/xflags-macros/src/parse.rs
@@ -1,4 +1,4 @@
-use std::mem;
+use std::{fmt, mem};
#[cfg(not(test))]
use proc_macro::{Delimiter, TokenStream, TokenTree};
@@ -16,15 +16,41 @@ pub(crate) struct Error {
impl std::error::Error for Error {}
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.msg)
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.msg, f)
}
}
-pub(crate) fn parse(ts: TokenStream) -> Result<ast::XFlags> {
- let mut p = Parser::new(ts);
- xflags(&mut p)
+pub(crate) fn xflags(ts: TokenStream) -> Result<ast::XFlags> {
+ let p = &mut Parser::new(ts);
+ let src = if p.eat_keyword("src") { Some(p.expect_string()?) } else { None };
+ let doc = opt_doc(p)?;
+ let mut cmd = cmd(p)?;
+ cmd.doc = doc;
+ add_help(&mut cmd);
+ let res = ast::XFlags { src, cmd };
+ Ok(res)
+}
+
+pub(crate) fn parse_or_exit(ts: TokenStream) -> Result<ast::XFlags> {
+ let p = &mut Parser::new(ts);
+ let mut cmd = anon_cmd(p)?;
+ assert!(cmd.subcommands.is_empty());
+ add_help(&mut cmd);
+ let res = ast::XFlags { src: None, cmd };
+ Ok(res)
+}
+
+fn add_help(cmd: &mut ast::Cmd) {
+ let help = ast::Flag {
+ arity: ast::Arity::Optional,
+ name: "help".to_string(),
+ short: Some("h".to_string()),
+ doc: Some("Prints help information.".to_string()),
+ val: None,
+ };
+ cmd.flags.push(help);
}
macro_rules! format_err {
@@ -40,19 +66,25 @@ macro_rules! bail {
};
}
-fn xflags(p: &mut Parser) -> Result<ast::XFlags> {
- let src = if p.eat_keyword("src") { Some(p.expect_string()?) } else { None };
- let doc = opt_doc(p)?;
- let mut cmd = cmd(p)?;
- cmd.doc = doc;
- let res = ast::XFlags { src, cmd };
- Ok(res)
+fn anon_cmd(p: &mut Parser) -> Result<ast::Cmd> {
+ cmd_impl(p, true)
}
fn cmd(p: &mut Parser) -> Result<ast::Cmd> {
- p.expect_keyword("cmd")?;
+ cmd_impl(p, false)
+}
+
+fn cmd_impl(p: &mut Parser, anon: bool) -> Result<ast::Cmd> {
+ let name = if anon {
+ String::new()
+ } else {
+ p.expect_keyword("cmd")?;
+ cmd_name(p)?
+ };
+
+ let idx = p.idx;
+ p.idx += 1;
- let name = cmd_name(p)?;
let mut res = ast::Cmd {
name,
doc: None,
@@ -60,25 +92,16 @@ fn cmd(p: &mut Parser) -> Result<ast::Cmd> {
flags: Vec::new(),
subcommands: Vec::new(),
default: false,
+ idx,
};
- while !p.at_delim(Delimiter::Brace) {
- let doc = opt_doc(p)?;
- let arity = arity(p)?;
- match opt_val(p)? {
- Some(val) => {
- let arg = ast::Arg { arity, doc, val };
- res.args.push(arg);
- }
- None => bail!("expected ident"),
- }
+ if !anon {
+ p.enter_delim(Delimiter::Brace)?;
}
-
- p.enter_delim(Delimiter::Brace)?;
while !p.end() {
let doc = opt_doc(p)?;
- let default = p.eat_keyword("default");
- if default || p.at_keyword("cmd") {
+ let default = !anon && p.eat_keyword("default");
+ if !anon && (default || p.at_keyword("cmd")) {
let mut cmd = cmd(p)?;
cmd.doc = doc;
res.subcommands.push(cmd);
@@ -90,35 +113,56 @@ fn cmd(p: &mut Parser) -> Result<ast::Cmd> {
res.subcommands.rotate_right(1);
}
} else {
- let mut flag = flag(p)?;
- flag.doc = doc;
- res.flags.push(flag);
+ let arity = arity(p)?;
+ let is_val = p.lookahead_punct(':', 1);
+ let name = p.expect_name()?;
+ if name.starts_with('-') {
+ let mut flag = flag(p, name)?;
+ flag.doc = doc;
+ flag.arity = arity;
+ res.flags.push(flag)
+ } else if is_val {
+ p.expect_punct(':')?;
+ let ty = ty(p)?;
+ let val = ast::Val { name, ty };
+ let arg = ast::Arg { arity, doc, val };
+ res.args.push(arg);
+ } else {
+ bail!("expected `--flag` or `arg: Type`")
+ }
}
}
- p.exit_delim()?;
+ if !anon {
+ p.exit_delim()?;
+ }
Ok(res)
}
-fn flag(p: &mut Parser) -> Result<ast::Flag> {
- let arity = arity(p)?;
-
- let mut short = None;
- let mut name = flag_name(p)?;
- if !name.starts_with("--") {
+fn flag(p: &mut Parser, name: String) -> Result<ast::Flag> {
+ let short;
+ let long;
+ if name.starts_with("--") {
+ short = None;
+ long = name;
+ } else {
short = Some(name);
if !p.eat_punct(',') {
bail!("long option is required for `{}`", short.unwrap());
}
- name = flag_name(p)?;
- if !name.starts_with("--") {
- bail!("long name must begin with `--`: `{}`", name);
+ long = flag_name(p)?;
+ if !long.starts_with("--") {
+ bail!("long name must begin with `--`: `{long}`");
}
}
+ if long == "--help" {
+ bail!("`--help` flag is generated automatically")
+ }
+
let val = opt_val(p)?;
Ok(ast::Flag {
- arity,
- name: name[2..].to_string(),
+ arity: ast::Arity::Required,
+ name: long[2..].to_string(),
short: short.map(|it| it[1..].to_string()),
doc: None,
val,
@@ -148,7 +192,7 @@ fn arity(p: &mut Parser) -> Result<ast::Arity> {
return Ok(ast::Arity::Repeated);
}
if let Some(name) = p.eat_name() {
- bail!("expected one of `optional`, `required`, `repeated`, got `{}`", name)
+ bail!("expected one of `optional`, `required`, `repeated`, got `{name}`")
}
bail!("expected one of `optional`, `required`, `repeated`, got {:?}", p.ts.pop())
}
@@ -193,7 +237,7 @@ fn opt_doc(p: &mut Parser) -> Result<Option<String>> {
fn cmd_name(p: &mut Parser) -> Result<String> {
let name = p.expect_name()?;
if name.starts_with('-') {
- bail!("command name can't begin with `-`: `{}`", name);
+ bail!("command name can't begin with `-`: `{name}`");
}
Ok(name)
}
@@ -201,7 +245,7 @@ fn cmd_name(p: &mut Parser) -> Result<String> {
fn flag_name(p: &mut Parser) -> Result<String> {
let name = p.expect_name()?;
if !name.starts_with('-') {
- bail!("flag name should begin with `-`: `{}`", name);
+ bail!("flag name should begin with `-`: `{name}`");
}
Ok(name)
}
@@ -209,21 +253,16 @@ fn flag_name(p: &mut Parser) -> Result<String> {
struct Parser {
stack: Vec<Vec<TokenTree>>,
ts: Vec<TokenTree>,
+ idx: u8,
}
impl Parser {
fn new(ts: TokenStream) -> Self {
let mut ts = ts.into_iter().collect::<Vec<_>>();
ts.reverse();
- Self { stack: Vec::new(), ts }
+ Self { stack: Vec::new(), ts, idx: 0 }
}
- fn at_delim(&mut self, delimiter: Delimiter) -> bool {
- match self.ts.last() {
- Some(TokenTree::Group(g)) => g.delimiter() == delimiter,
- _ => false,
- }
- }
fn enter_delim(&mut self, delimiter: Delimiter) -> Result<()> {
match self.ts.pop() {
Some(TokenTree::Group(g)) if g.delimiter() == delimiter => {
@@ -249,7 +288,7 @@ impl Parser {
fn expect_keyword(&mut self, kw: &str) -> Result<()> {
if !self.eat_keyword(kw) {
- bail!("expected `{}`", kw)
+ bail!("expected `{kw}`")
}
Ok(())
}
@@ -271,7 +310,7 @@ impl Parser {
fn expect_name(&mut self) -> Result<String> {
self.eat_name().ok_or_else(|| {
let next = self.ts.pop().map(|it| it.to_string()).unwrap_or_default();
- format_err!("expected a name, got: `{}`", next)
+ format_err!("expected a name, got: `{next}`")
})
}
fn eat_name(&mut self) -> Option<String> {
@@ -307,7 +346,7 @@ impl Parser {
fn expect_punct(&mut self, punct: char) -> Result<()> {
if !self.eat_punct(punct) {
- bail!("expected `{}`", punct)
+ bail!("expected `{punct}`")
}
Ok(())
}
@@ -330,11 +369,18 @@ impl Parser {
fn expect_string(&mut self) -> Result<String> {
match self.ts.pop() {
Some(TokenTree::Literal(lit)) if lit.to_string().starts_with('"') => {
- let text = lit.to_string();
- let res = text.trim_matches('"').to_string();
+ let res = str_lit_value(lit.to_string());
Ok(res)
}
_ => bail!("expected a string"),
}
}
}
+
+/// "Parser" a string literal into the corresponding value.
+///
+/// Really needs support in the proc_macro library:
+/// <https://internals.rust-lang.org/t/getting-value-out-of-proc-macro-literal/14140>
+fn str_lit_value(lit: String) -> String {
+ lit.trim_matches('"').replace("\\'", "'")
+}
diff --git a/vendor/xflags-macros/src/update.rs b/vendor/xflags-macros/src/update.rs
index e476e37aa..83a404c93 100644
--- a/vendor/xflags-macros/src/update.rs
+++ b/vendor/xflags-macros/src/update.rs
@@ -6,8 +6,7 @@ pub(crate) fn in_place(api: &str, path: &Path) {
Path::new(&dir).join(path)
};
- let mut text =
- fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read {:?}", path));
+ let mut text = fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read {path:?}"));
let (insert_to, indent) = locate(&text);