summaryrefslogtreecommitdiffstats
path: root/vendor/xflags-macros
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/xflags-macros
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/xflags-macros')
-rw-r--r--vendor/xflags-macros/.cargo-checksum.json1
-rw-r--r--vendor/xflags-macros/Cargo.toml27
-rw-r--r--vendor/xflags-macros/src/ast.rs51
-rw-r--r--vendor/xflags-macros/src/emit.rs544
-rw-r--r--vendor/xflags-macros/src/lib.rs26
-rw-r--r--vendor/xflags-macros/src/parse.rs340
-rw-r--r--vendor/xflags-macros/src/update.rs98
-rw-r--r--vendor/xflags-macros/tests/it/help.rs144
-rw-r--r--vendor/xflags-macros/tests/it/main.rs198
-rw-r--r--vendor/xflags-macros/tests/it/repeated_pos.rs94
-rw-r--r--vendor/xflags-macros/tests/it/smoke.rs112
-rw-r--r--vendor/xflags-macros/tests/it/src/help.rs25
-rw-r--r--vendor/xflags-macros/tests/it/src/repeated_pos.rs9
-rw-r--r--vendor/xflags-macros/tests/it/src/smoke.rs15
-rw-r--r--vendor/xflags-macros/tests/it/src/subcommands.rs20
-rw-r--r--vendor/xflags-macros/tests/it/subcommands.rs225
16 files changed, 1929 insertions, 0 deletions
diff --git a/vendor/xflags-macros/.cargo-checksum.json b/vendor/xflags-macros/.cargo-checksum.json
new file mode 100644
index 000000000..98f5dfe97
--- /dev/null
+++ b/vendor/xflags-macros/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"23f3d89d0198bfa3552286396432e2ace70627773527eaaf62ed4d3ef1b37166","src/ast.rs":"bf42c4e474893d5dbd4fa3c9b392d3f837219fabb2042cfc26acc08b2ddc6f4a","src/emit.rs":"390be2d24bc21d5644c9c69624fd263bfa7dc0f3081799481177a45952168d51","src/lib.rs":"f00a5c9e671a82263d4cf4cced2895bb93ae03220112bc385553573cf9c4cd05","src/parse.rs":"2c2c9118d1f0447a946a78753edb65edd6eeb4f2a27707f5601232c8880aba85","src/update.rs":"feb2cefca834dad6550b6539be7ec9b98fedb608cd09aeddf808350c638d67b8","tests/it/help.rs":"53dbb8b5c6e09649143dc8f9e5cd690db776271a1ea778726146fc04be8583f4","tests/it/main.rs":"3cde1afb05f2470642f0f590cbed24fb553bd0ac1da0fcae93a02ffe42247d97","tests/it/repeated_pos.rs":"1b2b922beb182899504cd36440fd1547026a8a5ef4f79b483a2695227f06c4a8","tests/it/smoke.rs":"5d304f8b24716d63081965914bf4bcf9fc8bfccf30d3f49fb9d71e3405917fd2","tests/it/src/help.rs":"7430f8773b5a8dc3607fbadc04d562f015f056062d373897675140e591da4b60","tests/it/src/repeated_pos.rs":"161b39be8e543ef6dd3f019162c85b9e6d22c30dcd6297e947a2872ed1bc3c5f","tests/it/src/smoke.rs":"f17d4ffb6177fc83aa7f0ad235a487a15490a9f646e7789af8a92db4ea9552fe","tests/it/src/subcommands.rs":"1147d1a7de73cb20d5604a3e08ce43b32a89dbb9ee37aa8c595f6138a083abb9","tests/it/subcommands.rs":"dab409a1b14755122ea42a3169d4a020ce1e548e52a56211874efeb73551de08"},"package":"45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022"} \ No newline at end of file
diff --git a/vendor/xflags-macros/Cargo.toml b/vendor/xflags-macros/Cargo.toml
new file mode 100644
index 000000000..a224e51a6
--- /dev/null
+++ b/vendor/xflags-macros/Cargo.toml
@@ -0,0 +1,27 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "xflags-macros"
+version = "0.2.4"
+authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
+description = "Private implementation details of xflags."
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/matklad/xflags"
+
+[lib]
+proc-macro = true
+[dev-dependencies.expect-test]
+version = "1"
+
+[dev-dependencies.proc-macro2]
+version = "1"
diff --git a/vendor/xflags-macros/src/ast.rs b/vendor/xflags-macros/src/ast.rs
new file mode 100644
index 000000000..5ccebd951
--- /dev/null
+++ b/vendor/xflags-macros/src/ast.rs
@@ -0,0 +1,51 @@
+#[derive(Debug)]
+pub(crate) struct XFlags {
+ pub(crate) src: Option<String>,
+ pub(crate) cmd: Cmd,
+}
+
+#[derive(Debug)]
+pub(crate) struct Cmd {
+ pub(crate) name: String,
+ pub(crate) doc: Option<String>,
+ pub(crate) args: Vec<Arg>,
+ pub(crate) flags: Vec<Flag>,
+ pub(crate) subcommands: Vec<Cmd>,
+ pub(crate) default: bool,
+}
+
+#[derive(Debug)]
+pub(crate) struct Arg {
+ pub(crate) arity: Arity,
+ pub(crate) doc: Option<String>,
+ pub(crate) val: Val,
+}
+
+#[derive(Debug)]
+pub(crate) struct Flag {
+ pub(crate) arity: Arity,
+ pub(crate) name: String,
+ pub(crate) short: Option<String>,
+ pub(crate) doc: Option<String>,
+ pub(crate) val: Option<Val>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum Arity {
+ Optional,
+ Required,
+ Repeated,
+}
+
+#[derive(Debug)]
+pub(crate) struct Val {
+ pub(crate) name: String,
+ pub(crate) ty: Ty,
+}
+
+#[derive(Debug)]
+pub(crate) enum Ty {
+ PathBuf,
+ OsString,
+ FromStr(String),
+}
diff --git a/vendor/xflags-macros/src/emit.rs b/vendor/xflags-macros/src/emit.rs
new file mode 100644
index 000000000..05d5f01dd
--- /dev/null
+++ b/vendor/xflags-macros/src/emit.rs
@@ -0,0 +1,544 @@
+use crate::{ast, update};
+
+use std::{fmt::Write, path::Path};
+
+pub(crate) fn emit(xflags: &ast::XFlags) -> String {
+ let mut buf = String::new();
+
+ emit_cmd(&mut buf, &xflags.cmd);
+ blank_line(&mut buf);
+ emit_api(&mut buf, xflags);
+
+ if std::env::var("UPDATE_XFLAGS").is_ok() {
+ if let Some(src) = &xflags.src {
+ update::in_place(&buf, Path::new(src.as_str()))
+ } else {
+ update::stdout(&buf);
+ }
+ }
+
+ if xflags.src.is_some() {
+ buf.clear()
+ }
+
+ blank_line(&mut buf);
+ emit_impls(&mut buf, &xflags);
+ emit_help(&mut buf, xflags);
+
+ buf
+}
+
+macro_rules! w {
+ ($($tt:tt)*) => {
+ drop(write!($($tt)*))
+ };
+}
+
+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() {
+ w!(buf, ";\n");
+ return;
+ }
+ w!(buf, " {{\n");
+
+ for arg in &cmd.args {
+ let ty = gen_arg_ty(arg.arity, &arg.val.ty);
+ w!(buf, " pub {}: {},\n", arg.val.ident(), ty);
+ }
+
+ if !cmd.args.is_empty() && !cmd.flags.is_empty() {
+ blank_line(buf);
+ }
+
+ for flag in &cmd.flags {
+ let ty = gen_flag_ty(flag.arity, flag.val.as_ref().map(|it| &it.ty));
+ w!(buf, " pub {}: {},\n", flag.ident(), ty);
+ }
+
+ if cmd.has_subcommands() {
+ w!(buf, " pub subcommand: {},\n", cmd.cmd_enum_ident());
+ }
+ w!(buf, "}}\n");
+
+ if cmd.has_subcommands() {
+ blank_line(buf);
+ 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);
+ }
+ w!(buf, "}}\n");
+
+ for sub in &cmd.subcommands {
+ blank_line(buf);
+ emit_cmd(buf, sub);
+ }
+ }
+}
+
+fn gen_flag_ty(arity: ast::Arity, ty: Option<&ast::Ty>) -> String {
+ match ty {
+ None => match arity {
+ ast::Arity::Optional => "bool".to_string(),
+ ast::Arity::Required => "()".to_string(),
+ ast::Arity::Repeated => "u32".to_string(),
+ },
+ Some(ty) => gen_arg_ty(arity, ty),
+ }
+}
+
+fn gen_arg_ty(arity: ast::Arity, ty: &ast::Ty) -> String {
+ let ty = match ty {
+ ast::Ty::PathBuf => "PathBuf".into(),
+ ast::Ty::OsString => "OsString".into(),
+ ast::Ty::FromStr(it) => it.clone(),
+ };
+ match arity {
+ ast::Arity::Optional => format!("Option<{}>", ty),
+ ast::Arity::Required => ty,
+ ast::Arity::Repeated => format!("Vec<{}>", ty),
+ }
+}
+
+fn emit_api(buf: &mut String, xflags: &ast::XFlags) {
+ w!(buf, "impl {} {{\n", camel(&xflags.cmd.name));
+
+ w!(buf, " pub const HELP: &'static str = Self::HELP_;\n");
+ blank_line(buf);
+
+ w!(buf, " #[allow(dead_code)]\n");
+ w!(buf, " pub fn from_env() -> xflags::Result<Self> {{\n");
+ w!(buf, " Self::from_env_()\n");
+ w!(buf, " }}\n");
+ blank_line(buf);
+
+ w!(buf, " #[allow(dead_code)]\n");
+ w!(buf, " pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {{\n");
+ w!(buf, " Self::from_vec_(args)\n");
+ w!(buf, " }}\n");
+ w!(buf, "}}\n");
+}
+
+fn emit_impls(buf: &mut String, xflags: &ast::XFlags) -> () {
+ w!(buf, "impl {} {{\n", camel(&xflags.cmd.name));
+ 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");
+ w!(buf, " }}\n");
+ w!(buf, " fn from_vec_(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {{\n");
+ w!(buf, " let mut p = xflags::rt::Parser::new(args);\n");
+ w!(buf, " Self::parse_(&mut p)\n");
+ 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);
+ }
+}
+
+fn emit_impl(buf: &mut String, cmd: &ast::Cmd) -> () {
+ w!(buf, "impl {} {{\n", camel(&cmd.name));
+ w!(buf, "fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {{\n");
+
+ for flag in &cmd.flags {
+ w!(buf, "let mut {} = Vec::new();\n", flag.ident());
+ }
+ blank_line(buf);
+
+ if !cmd.args.is_empty() {
+ for arg in &cmd.args {
+ w!(buf, "let mut {} = (false, Vec::new());\n", arg.val.ident());
+ }
+ blank_line(buf);
+ }
+
+ if cmd.has_subcommands() {
+ w!(buf, "let mut sub_ = None;");
+ blank_line(buf);
+ }
+
+ 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());
+ 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)
+ }
+ },
+ 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");
+ }
+
+ 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, "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, ");\n");
+ match arg.arity {
+ ast::Arity::Optional | ast::Arity::Required => {
+ w!(buf, "*done_ = true;\n");
+ }
+ ast::Arity::Repeated => (),
+ }
+ w!(buf, "continue;\n");
+ w!(buf, "}}\n");
+ }
+ if cmd.default_subcommand().is_some() {
+ w!(buf, "p_.push_back(Err(arg_)); break;");
+ } 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");
+ }
+
+ 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);
+ }
+
+ for flag in &cmd.flags {
+ w!(buf, "{}: ", flag.ident());
+ match &flag.val {
+ Some(_val) => match flag.arity {
+ ast::Arity::Optional => {
+ w!(buf, "p_.optional(\"--{}\", {})?", flag.name, flag.ident())
+ }
+ ast::Arity::Required => {
+ w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident())
+ }
+ ast::Arity::Repeated => w!(buf, "{}", flag.ident()),
+ },
+ None => match flag.arity {
+ ast::Arity::Optional => {
+ w!(buf, "p_.optional(\"--{}\", {})?.is_some()", flag.name, flag.ident())
+ }
+ ast::Arity::Required => {
+ w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident())
+ }
+ ast::Arity::Repeated => w!(buf, "{}.len() as u32", flag.ident()),
+ },
+ }
+ w!(buf, ",\n");
+ }
+ if cmd.has_subcommands() {
+ w!(buf, "subcommand: p_.subcommand(sub_)?,\n");
+ }
+ w!(buf, "}})\n");
+
+ w!(buf, "}}\n");
+ w!(buf, "}}\n");
+}
+
+fn emit_help(buf: &mut String, xflags: &ast::XFlags) {
+ w!(buf, "impl {} {{\n", xflags.cmd.ident());
+
+ let help = {
+ let mut buf = String::new();
+ help_rec(&mut buf, "", &xflags.cmd);
+ buf
+ };
+ let help = format!("{:?}", help);
+ let help = help.replace("\\n", "\n").replacen("\"", "\"\\\n", 1);
+
+ w!(buf, "const HELP_: &'static str = {};", help);
+ w!(buf, "}}\n");
+}
+
+fn write_lines_indented(buf: &mut String, multiline_str: &str, indent: usize) {
+ for line in multiline_str.split('\n').map(str::trim_end) {
+ if line.is_empty() {
+ w!(buf, "\n")
+ } else {
+ w!(buf, "{blank:indent$}{}\n", line, indent = indent, blank = "");
+ }
+ }
+}
+
+fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) {
+ w!(buf, "{}{}\n", prefix, cmd.name);
+ if let Some(doc) = &cmd.doc {
+ 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);
+ w!(buf, "{}ARGS:\n", indent);
+
+ let mut blank = "";
+ for arg in &args {
+ w!(buf, "{}", blank);
+ blank = "\n";
+
+ let (l, r) = match arg.arity {
+ ast::Arity::Optional => ("[", "]"),
+ ast::Arity::Required => ("<", ">"),
+ ast::Arity::Repeated => ("<", ">..."),
+ };
+ w!(buf, " {}{}{}\n", l, arg.val.name, r);
+ if let Some(doc) = &arg.doc {
+ write_lines_indented(buf, doc, 6)
+ }
+ }
+ }
+
+ let flags = cmd.flags_with_default();
+ if !flags.is_empty() {
+ blank_line(buf);
+ w!(buf, "{}OPTIONS:\n", indent);
+
+ let mut blank = "";
+ for flag in &flags {
+ w!(buf, "{}", blank);
+ blank = "\n";
+
+ 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);
+ if let Some(doc) = &flag.doc {
+ write_lines_indented(buf, doc, 6);
+ }
+ }
+ }
+
+ let subcommands = cmd.named_subcommands();
+ if !subcommands.is_empty() {
+ if prefix.is_empty() {
+ blank_line(buf);
+ w!(buf, "SUBCOMMANDS:");
+ }
+
+ let prefix = format!("{}{} ", prefix, cmd.name);
+ for sub in subcommands {
+ blank_line(buf);
+ blank_line(buf);
+ help_rec(buf, &prefix, sub);
+ }
+ }
+}
+
+impl ast::Cmd {
+ fn ident(&self) -> String {
+ camel(&self.name)
+ }
+ fn cmd_enum_ident(&self) -> String {
+ format!("{}Cmd", self.ident())
+ }
+ fn has_subcommands(&self) -> bool {
+ !self.subcommands.is_empty()
+ }
+ fn named_subcommands(&self) -> &[ast::Cmd] {
+ let start = if self.default { 1 } else { 0 };
+ &self.subcommands[start..]
+ }
+ fn default_subcommand(&self) -> Option<&ast::Cmd> {
+ if self.default {
+ self.subcommands.first()
+ } else {
+ None
+ }
+ }
+ fn args_with_default(&self) -> Vec<&ast::Arg> {
+ let mut res = self.args.iter().collect::<Vec<_>>();
+ if let Some(sub) = self.default_subcommand() {
+ res.extend(sub.args_with_default());
+ }
+ res
+ }
+ fn flags_with_default(&self) -> Vec<&ast::Flag> {
+ let mut res = self.flags.iter().collect::<Vec<_>>();
+ if let Some(sub) = self.default_subcommand() {
+ res.extend(sub.flags_with_default())
+ }
+ res
+ }
+}
+
+impl ast::Flag {
+ fn ident(&self) -> String {
+ snake(&self.name)
+ }
+}
+
+impl ast::Val {
+ fn ident(&self) -> String {
+ snake(&self.name)
+ }
+}
+
+fn blank_line(buf: &mut String) {
+ w!(buf, "\n");
+}
+
+fn camel(s: &str) -> String {
+ s.split('-').map(first_upper).collect()
+}
+
+fn first_upper(s: &str) -> String {
+ s.chars()
+ .next()
+ .map(|it| it.to_ascii_uppercase())
+ .into_iter()
+ .chain(s.chars().skip(1))
+ .collect()
+}
+
+fn snake(s: &str) -> String {
+ s.replace('-', "_")
+}
+
+#[cfg(test)]
+mod tests {
+ use std::{
+ fs,
+ io::Write,
+ path::Path,
+ process::{Command, Stdio},
+ };
+
+ fn reformat(text: String) -> Option<String> {
+ let mut rustfmt =
+ Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();
+ let mut stdin = rustfmt.stdin.take().unwrap();
+ stdin.write_all(text.as_bytes()).unwrap();
+ drop(stdin);
+ let out = rustfmt.wait_with_output().unwrap();
+ let res = String::from_utf8(out.stdout).unwrap();
+ if res.is_empty() {
+ None
+ } else {
+ Some(res)
+ }
+ }
+
+ fn update_on_disk_if_different(file: &Path, new_contents: String) -> bool {
+ let old_contents = fs::read_to_string(file).unwrap_or_default();
+ if old_contents.trim() == new_contents.trim() {
+ return false;
+ }
+ fs::write(file, new_contents).unwrap();
+ true
+ }
+
+ #[test]
+ fn gen_it() {
+ let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/it");
+
+ let mut did_update = false;
+ for entry in fs::read_dir(test_dir.join("src")).unwrap() {
+ let entry = entry.unwrap();
+
+ let text = fs::read_to_string(entry.path()).unwrap();
+ let mut lines = text.lines().collect::<Vec<_>>();
+ lines.pop();
+ lines.remove(0);
+ let text = lines.join("\n");
+
+ let res = crate::compile(&text);
+ let fmt = reformat(res.clone());
+
+ let code = format!(
+ "#![allow(unused)]\nuse std::{{ffi::OsString, path::PathBuf}};\n\n{}",
+ fmt.as_deref().unwrap_or(&res)
+ );
+
+ let name = entry.file_name();
+ did_update |= update_on_disk_if_different(&test_dir.join(name), code);
+
+ if fmt.is_none() {
+ panic!("syntax error");
+ }
+ }
+ if did_update {
+ panic!("generated output changed")
+ }
+ }
+}
diff --git a/vendor/xflags-macros/src/lib.rs b/vendor/xflags-macros/src/lib.rs
new file mode 100644
index 000000000..8fac00587
--- /dev/null
+++ b/vendor/xflags-macros/src/lib.rs
@@ -0,0 +1,26 @@
+mod ast;
+mod parse;
+mod emit;
+mod update;
+
+#[proc_macro]
+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);
+ text.parse().unwrap()
+ }
+ #[cfg(test)]
+ 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();
+ emit::emit(&cmd)
+}
diff --git a/vendor/xflags-macros/src/parse.rs b/vendor/xflags-macros/src/parse.rs
new file mode 100644
index 000000000..e9749b141
--- /dev/null
+++ b/vendor/xflags-macros/src/parse.rs
@@ -0,0 +1,340 @@
+use std::mem;
+
+#[cfg(not(test))]
+use proc_macro::{Delimiter, TokenStream, TokenTree};
+#[cfg(test)]
+use proc_macro2::{Delimiter, TokenStream, TokenTree};
+
+use crate::ast;
+
+type Result<T, E = Error> = std::result::Result<T, E>;
+
+#[derive(Debug)]
+pub(crate) struct Error {
+ msg: String,
+}
+
+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)
+ }
+}
+
+pub(crate) fn parse(ts: TokenStream) -> Result<ast::XFlags> {
+ let mut p = Parser::new(ts);
+ xflags(&mut p)
+}
+
+macro_rules! format_err {
+ ($($tt:tt)*) => {
+ Error { msg: format!($($tt)*) }
+ // panic!($($tt)*)
+ };
+}
+
+macro_rules! bail {
+ ($($tt:tt)*) => {
+ return Err(format_err!($($tt)*))
+ };
+}
+
+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 cmd(p: &mut Parser) -> Result<ast::Cmd> {
+ p.expect_keyword("cmd")?;
+
+ let name = cmd_name(p)?;
+ let mut res = ast::Cmd {
+ name,
+ doc: None,
+ args: Vec::new(),
+ flags: Vec::new(),
+ subcommands: Vec::new(),
+ default: false,
+ };
+
+ 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"),
+ }
+ }
+
+ 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 mut cmd = cmd(p)?;
+ cmd.doc = doc;
+ res.subcommands.push(cmd);
+ if default {
+ if res.default {
+ bail!("only one subcommand can be default")
+ }
+ res.default = true;
+ res.subcommands.rotate_right(1);
+ }
+ } else {
+ let mut flag = flag(p)?;
+ flag.doc = doc;
+ res.flags.push(flag);
+ }
+ }
+ 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("--") {
+ 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);
+ }
+ }
+
+ let val = opt_val(p)?;
+ Ok(ast::Flag {
+ arity,
+ name: name[2..].to_string(),
+ short: short.map(|it| it[1..].to_string()),
+ doc: None,
+ val,
+ })
+}
+
+fn opt_val(p: &mut Parser) -> Result<Option<ast::Val>, Error> {
+ if !p.lookahead_punct(':', 1) {
+ return Ok(None);
+ }
+
+ let name = p.expect_name()?;
+ p.expect_punct(':')?;
+ let ty = ty(p)?;
+ let res = ast::Val { name, ty };
+ Ok(Some(res))
+}
+
+fn arity(p: &mut Parser) -> Result<ast::Arity> {
+ if p.eat_keyword("optional") {
+ return Ok(ast::Arity::Optional);
+ }
+ if p.eat_keyword("required") {
+ return Ok(ast::Arity::Required);
+ }
+ if p.eat_keyword("repeated") {
+ 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 {:?}", p.ts.pop())
+}
+
+fn ty(p: &mut Parser) -> Result<ast::Ty> {
+ let name = p.expect_name()?;
+ let res = match name.as_str() {
+ "PathBuf" => ast::Ty::PathBuf,
+ "OsString" => ast::Ty::OsString,
+ _ => ast::Ty::FromStr(name),
+ };
+ Ok(res)
+}
+
+fn opt_single_doc(p: &mut Parser) -> Result<Option<String>> {
+ if !p.eat_punct('#') {
+ return Ok(None);
+ }
+ p.enter_delim(Delimiter::Bracket)?;
+ p.expect_keyword("doc")?;
+ p.expect_punct('=')?;
+ let mut res = p.expect_string()?;
+ if let Some(suf) = res.strip_prefix(' ') {
+ res = suf.to_string();
+ }
+ p.exit_delim()?;
+ Ok(Some(res))
+}
+
+fn opt_doc(p: &mut Parser) -> Result<Option<String>> {
+ let lines =
+ core::iter::from_fn(|| opt_single_doc(p).transpose()).collect::<Result<Vec<String>>>()?;
+ let lines = lines.join("\n");
+
+ if lines.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(lines))
+ }
+}
+
+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);
+ }
+ Ok(name)
+}
+
+fn flag_name(p: &mut Parser) -> Result<String> {
+ let name = p.expect_name()?;
+ if !name.starts_with('-') {
+ bail!("flag name should begin with `-`: `{}`", name);
+ }
+ Ok(name)
+}
+
+struct Parser {
+ stack: Vec<Vec<TokenTree>>,
+ ts: Vec<TokenTree>,
+}
+
+impl Parser {
+ fn new(ts: TokenStream) -> Self {
+ let mut ts = ts.into_iter().collect::<Vec<_>>();
+ ts.reverse();
+ Self { stack: Vec::new(), ts }
+ }
+
+ 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 => {
+ let mut ts = g.stream().into_iter().collect::<Vec<_>>();
+ ts.reverse();
+ let ts = mem::replace(&mut self.ts, ts);
+ self.stack.push(ts);
+ }
+ _ => bail!("expected `{{`"),
+ }
+ Ok(())
+ }
+ fn exit_delim(&mut self) -> Result<()> {
+ if !self.end() {
+ bail!("expected `}}`")
+ }
+ self.ts = self.stack.pop().unwrap();
+ Ok(())
+ }
+ fn end(&mut self) -> bool {
+ self.ts.last().is_none()
+ }
+
+ fn expect_keyword(&mut self, kw: &str) -> Result<()> {
+ if !self.eat_keyword(kw) {
+ bail!("expected `{}`", kw)
+ }
+ Ok(())
+ }
+ fn eat_keyword(&mut self, kw: &str) -> bool {
+ if self.at_keyword(kw) {
+ self.ts.pop().unwrap();
+ true
+ } else {
+ false
+ }
+ }
+ fn at_keyword(&mut self, kw: &str) -> bool {
+ match self.ts.last() {
+ Some(TokenTree::Ident(ident)) => &ident.to_string() == kw,
+ _ => false,
+ }
+ }
+
+ 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)
+ })
+ }
+ fn eat_name(&mut self) -> Option<String> {
+ let mut buf = String::new();
+ let mut prev_ident = false;
+ loop {
+ match self.ts.last() {
+ Some(TokenTree::Punct(p)) if p.as_char() == '-' => {
+ prev_ident = false;
+ buf.push('-');
+ }
+ Some(TokenTree::Ident(ident)) if !prev_ident => {
+ prev_ident = true;
+ buf.push_str(&ident.to_string());
+ }
+ _ => break,
+ }
+ self.ts.pop();
+ }
+ if buf.is_empty() {
+ None
+ } else {
+ Some(buf)
+ }
+ }
+
+ fn _expect_ident(&mut self) -> Result<String> {
+ match self.ts.pop() {
+ Some(TokenTree::Ident(ident)) => Ok(ident.to_string()),
+ _ => bail!("expected ident"),
+ }
+ }
+
+ fn expect_punct(&mut self, punct: char) -> Result<()> {
+ if !self.eat_punct(punct) {
+ bail!("expected `{}`", punct)
+ }
+ Ok(())
+ }
+ fn eat_punct(&mut self, punct: char) -> bool {
+ match self.ts.last() {
+ Some(TokenTree::Punct(p)) if p.as_char() == punct => {
+ self.ts.pop();
+ true
+ }
+ _ => false,
+ }
+ }
+ fn lookahead_punct(&mut self, punct: char, n: usize) -> bool {
+ match self.ts.iter().rev().nth(n) {
+ Some(TokenTree::Punct(p)) => p.as_char() == punct,
+ _ => false,
+ }
+ }
+
+ 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();
+ Ok(res)
+ }
+ _ => bail!("expected a string"),
+ }
+ }
+}
diff --git a/vendor/xflags-macros/src/update.rs b/vendor/xflags-macros/src/update.rs
new file mode 100644
index 000000000..e476e37aa
--- /dev/null
+++ b/vendor/xflags-macros/src/update.rs
@@ -0,0 +1,98 @@
+use std::{fs, ops::Range, path::Path};
+
+pub(crate) fn in_place(api: &str, path: &Path) {
+ let path = {
+ let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ Path::new(&dir).join(path)
+ };
+
+ let mut text =
+ fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read {:?}", path));
+
+ let (insert_to, indent) = locate(&text);
+
+ let api: String =
+ with_preamble(api)
+ .lines()
+ .map(|it| {
+ if it.trim().is_empty() {
+ "\n".to_string()
+ } else {
+ format!("{}{}\n", indent, it)
+ }
+ })
+ .collect();
+ text.replace_range(insert_to, &api);
+
+ fs::write(&path, text.as_bytes()).unwrap();
+}
+
+pub(crate) fn stdout(api: &str) {
+ print!("{}", with_preamble(api))
+}
+
+fn with_preamble(api: &str) -> String {
+ format!(
+ "\
+// generated start
+// The following code is generated by `xflags` macro.
+// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
+{}
+// generated end
+",
+ api.trim()
+ )
+}
+
+fn locate(text: &str) -> (Range<usize>, String) {
+ if let Some(it) = locate_existing(text) {
+ return it;
+ }
+ if let Some(it) = locate_new(text) {
+ return it;
+ }
+ panic!("failed to update xflags in place")
+}
+
+fn locate_existing(text: &str) -> Option<(Range<usize>, String)> {
+ let start_idx = text.find("// generated start")?;
+ let start_idx = newline_before(text, start_idx);
+
+ let end_idx = text.find("// generated end")?;
+ let end_idx = newline_after(text, end_idx);
+
+ let indent = indent_at(text, start_idx);
+
+ Some((start_idx..end_idx, indent))
+}
+
+fn newline_before(text: &str, start_idx: usize) -> usize {
+ text[..start_idx].rfind('\n').map_or(start_idx, |it| it + 1)
+}
+
+fn newline_after(text: &str, start_idx: usize) -> usize {
+ start_idx + text[start_idx..].find('\n').map_or(text[start_idx..].len(), |it| it + 1)
+}
+
+fn indent_at(text: &str, start_idx: usize) -> String {
+ text[start_idx..].chars().take_while(|&it| it == ' ').collect()
+}
+
+fn locate_new(text: &str) -> Option<(Range<usize>, String)> {
+ let mut idx = text.find("xflags!")?;
+ let mut lvl = 0i32;
+ for c in text[idx..].chars() {
+ idx += c.len_utf8();
+ match c {
+ '{' => lvl += 1,
+ '}' if lvl == 1 => break,
+ '}' => lvl -= 1,
+ _ => (),
+ }
+ }
+ let indent = indent_at(text, newline_before(text, idx));
+ if text[idx..].starts_with('\n') {
+ idx += 1;
+ }
+ Some((idx..idx, indent))
+}
diff --git a/vendor/xflags-macros/tests/it/help.rs b/vendor/xflags-macros/tests/it/help.rs
new file mode 100644
index 000000000..36a966485
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/help.rs
@@ -0,0 +1,144 @@
+#![allow(unused)]
+use std::{ffi::OsString, path::PathBuf};
+
+#[derive(Debug)]
+pub struct Helpful {
+ pub src: Option<PathBuf>,
+ pub extra: Option<String>,
+
+ pub switch: (),
+ pub subcommand: HelpfulCmd,
+}
+
+#[derive(Debug)]
+pub enum HelpfulCmd {
+ Sub(Sub),
+}
+
+#[derive(Debug)]
+pub struct Sub {
+ pub flag: bool,
+}
+
+impl Helpful {
+ pub const HELP: &'static str = Self::HELP_;
+
+ #[allow(dead_code)]
+ pub fn from_env() -> xflags::Result<Self> {
+ Self::from_env_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ Self::from_vec_(args)
+ }
+}
+
+impl Helpful {
+ fn from_env_() -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new_from_env();
+ Self::parse_(&mut p)
+ }
+ fn from_vec_(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new(args);
+ Self::parse_(&mut p)
+ }
+}
+
+impl Helpful {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut switch = Vec::new();
+
+ let mut src = (false, Vec::new());
+ let mut extra = (false, Vec::new());
+
+ let mut sub_ = None;
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--switch" | "-s" => switch.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ match arg_.to_str().unwrap_or("") {
+ "sub" => {
+ sub_ = Some(HelpfulCmd::Sub(Sub::parse_(p_)?));
+ break;
+ }
+ _ => (),
+ }
+ if let (done_ @ false, buf_) = &mut src {
+ buf_.push(arg_.into());
+ *done_ = true;
+ continue;
+ }
+ if let (done_ @ false, buf_) = &mut extra {
+ buf_.push(p_.value_from_str::<String>("extra", arg_)?);
+ *done_ = true;
+ continue;
+ }
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self {
+ src: p_.optional("src", src.1)?,
+ extra: p_.optional("extra", extra.1)?,
+
+ switch: p_.required("--switch", switch)?,
+ subcommand: p_.subcommand(sub_)?,
+ })
+ }
+}
+
+impl Sub {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut flag = Vec::new();
+
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--flag" | "-f" => flag.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self { flag: p_.optional("--flag", flag)?.is_some() })
+ }
+}
+impl Helpful {
+ const HELP_: &'static str = "\
+helpful
+ Does stuff
+
+ Helpful stuff.
+
+ARGS:
+ [src]
+ With an arg.
+
+ [extra]
+ Another arg.
+
+ This time, we provide some extra info about the
+ arg. Maybe some caveats, or what kinds of
+ values are accepted.
+
+OPTIONS:
+ -s, --switch
+ And a switch.
+
+SUBCOMMANDS:
+
+helpful sub
+ And even a subcommand!
+
+ OPTIONS:
+ -f, --flag
+ With an optional flag. This has a really long
+ description which spans multiple lines.
+";
+}
diff --git a/vendor/xflags-macros/tests/it/main.rs b/vendor/xflags-macros/tests/it/main.rs
new file mode 100644
index 000000000..1ce058814
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/main.rs
@@ -0,0 +1,198 @@
+mod repeated_pos;
+mod smoke;
+mod subcommands;
+mod help;
+
+use std::{ffi::OsString, fmt};
+
+use expect_test::{expect, Expect};
+
+fn check<F, A>(f: F, args: &str, expect: Expect)
+where
+ F: FnOnce(Vec<OsString>) -> xflags::Result<A>,
+ A: fmt::Debug,
+{
+ let args = args.split_ascii_whitespace().map(OsString::from).collect::<Vec<_>>();
+ let res = f(args);
+ match res {
+ Ok(args) => {
+ expect.assert_debug_eq(&args);
+ }
+ Err(err) => {
+ expect.assert_eq(&err.to_string());
+ }
+ }
+}
+
+#[test]
+fn smoke() {
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 92 .",
+ expect![[r#"
+ RustAnalyzer {
+ workspace: ".",
+ jobs: None,
+ log_file: None,
+ verbose: 0,
+ number: 92,
+ data: [],
+ emoji: false,
+ }
+ "#]],
+ );
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 92 -v --verbose -v --data 0xDEAD --log-file /tmp/log.txt --data 0xBEEF .",
+ expect![[r#"
+ RustAnalyzer {
+ workspace: ".",
+ jobs: None,
+ log_file: Some(
+ "/tmp/log.txt",
+ ),
+ verbose: 3,
+ number: 92,
+ data: [
+ "0xDEAD",
+ "0xBEEF",
+ ],
+ emoji: false,
+ }
+ "#]],
+ );
+
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 92 --werbose",
+ expect![[r#"unexpected flag: `--werbose`"#]],
+ );
+ check(smoke::RustAnalyzer::from_vec, "", expect![[r#"flag is required: `workspace`"#]]);
+ check(smoke::RustAnalyzer::from_vec, ".", expect![[r#"flag is required: `--number`"#]]);
+ check(smoke::RustAnalyzer::from_vec, "-n", expect![[r#"expected a value for `-n`"#]]);
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n lol",
+ expect![[r#"can't parse `-n`, invalid digit found in string"#]],
+ );
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 1 -n 2 .",
+ expect![[r#"flag specified more than once: `--number`"#]],
+ );
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 1 . 92 lol",
+ expect![[r#"unexpected argument: "lol""#]],
+ );
+ check(
+ smoke::RustAnalyzer::from_vec,
+ "-n 1 . --emoji --emoji",
+ expect![[r#"flag specified more than once: `--emoji`"#]],
+ );
+}
+
+#[test]
+fn repeated_argument() {
+ check(
+ repeated_pos::RepeatedPos::from_vec,
+ "a 11 c d e f",
+ expect![[r#"
+ RepeatedPos {
+ a: "a",
+ b: Some(
+ 11,
+ ),
+ c: Some(
+ "c",
+ ),
+ rest: [
+ "d",
+ "e",
+ "f",
+ ],
+ }
+ "#]],
+ );
+}
+
+#[test]
+fn subcommands() {
+ check(
+ subcommands::RustAnalyzer::from_vec,
+ "server",
+ expect![[r#"
+ RustAnalyzer {
+ verbose: 0,
+ subcommand: Server(
+ Server {
+ dir: None,
+ 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,
+ "server watch",
+ expect![[r#"
+ RustAnalyzer {
+ verbose: 0,
+ subcommand: Server(
+ Server {
+ dir: None,
+ subcommand: Watch(
+ Watch,
+ ),
+ },
+ ),
+ }
+ "#]],
+ );
+
+ check(
+ subcommands::RustAnalyzer::from_vec,
+ "-v analysis-stats . --parallel",
+ expect![[r#"
+ RustAnalyzer {
+ verbose: 1,
+ subcommand: AnalysisStats(
+ AnalysisStats {
+ path: ".",
+ parallel: true,
+ },
+ ),
+ }
+ "#]],
+ );
+
+ check(subcommands::RustAnalyzer::from_vec, "", expect![[r#"subcommand is required"#]]);
+}
diff --git a/vendor/xflags-macros/tests/it/repeated_pos.rs b/vendor/xflags-macros/tests/it/repeated_pos.rs
new file mode 100644
index 000000000..334af371d
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/repeated_pos.rs
@@ -0,0 +1,94 @@
+#![allow(unused)]
+use std::{ffi::OsString, path::PathBuf};
+
+#[derive(Debug)]
+pub struct RepeatedPos {
+ pub a: PathBuf,
+ pub b: Option<u32>,
+ pub c: Option<OsString>,
+ pub rest: Vec<OsString>,
+}
+
+impl RepeatedPos {
+ pub const HELP: &'static str = Self::HELP_;
+
+ #[allow(dead_code)]
+ pub fn from_env() -> xflags::Result<Self> {
+ Self::from_env_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ Self::from_vec_(args)
+ }
+}
+
+impl RepeatedPos {
+ fn from_env_() -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new_from_env();
+ Self::parse_(&mut p)
+ }
+ fn from_vec_(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new(args);
+ Self::parse_(&mut p)
+ }
+}
+
+impl RepeatedPos {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut a = (false, Vec::new());
+ let mut b = (false, Vec::new());
+ let mut c = (false, Vec::new());
+ let mut rest = (false, Vec::new());
+
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ if let (done_ @ false, buf_) = &mut a {
+ buf_.push(arg_.into());
+ *done_ = true;
+ continue;
+ }
+ if let (done_ @ false, buf_) = &mut b {
+ buf_.push(p_.value_from_str::<u32>("b", arg_)?);
+ *done_ = true;
+ continue;
+ }
+ if let (done_ @ false, buf_) = &mut c {
+ buf_.push(arg_.into());
+ *done_ = true;
+ continue;
+ }
+ if let (false, buf_) = &mut rest {
+ buf_.push(arg_.into());
+ continue;
+ }
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self {
+ a: p_.required("a", a.1)?,
+ b: p_.optional("b", b.1)?,
+ c: p_.optional("c", c.1)?,
+ rest: rest.1,
+ })
+ }
+}
+impl RepeatedPos {
+ const HELP_: &'static str = "\
+RepeatedPos
+
+ARGS:
+ <a>
+
+ [b]
+
+ [c]
+
+ <rest>...
+";
+}
diff --git a/vendor/xflags-macros/tests/it/smoke.rs b/vendor/xflags-macros/tests/it/smoke.rs
new file mode 100644
index 000000000..e22c4f1f6
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/smoke.rs
@@ -0,0 +1,112 @@
+#![allow(unused)]
+use std::{ffi::OsString, path::PathBuf};
+
+#[derive(Debug)]
+pub struct RustAnalyzer {
+ pub workspace: PathBuf,
+ pub jobs: Option<u32>,
+
+ pub log_file: Option<PathBuf>,
+ pub verbose: u32,
+ pub number: u32,
+ pub data: Vec<OsString>,
+ pub emoji: bool,
+}
+
+impl RustAnalyzer {
+ pub const HELP: &'static str = Self::HELP_;
+
+ #[allow(dead_code)]
+ pub fn from_env() -> xflags::Result<Self> {
+ Self::from_env_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ Self::from_vec_(args)
+ }
+}
+
+impl RustAnalyzer {
+ fn from_env_() -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new_from_env();
+ Self::parse_(&mut p)
+ }
+ fn from_vec_(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new(args);
+ Self::parse_(&mut p)
+ }
+}
+
+impl RustAnalyzer {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut log_file = Vec::new();
+ let mut verbose = Vec::new();
+ let mut number = Vec::new();
+ let mut data = Vec::new();
+ let mut emoji = Vec::new();
+
+ let mut workspace = (false, Vec::new());
+ let mut jobs = (false, Vec::new());
+
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--log-file" => log_file.push(p_.next_value(&flag_)?.into()),
+ "--verbose" | "-v" => verbose.push(()),
+ "--number" | "-n" => number.push(p_.next_value_from_str::<u32>(&flag_)?),
+ "--data" => data.push(p_.next_value(&flag_)?.into()),
+ "--emoji" => emoji.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ if let (done_ @ false, buf_) = &mut workspace {
+ buf_.push(arg_.into());
+ *done_ = true;
+ continue;
+ }
+ if let (done_ @ false, buf_) = &mut jobs {
+ buf_.push(p_.value_from_str::<u32>("jobs", arg_)?);
+ *done_ = true;
+ continue;
+ }
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self {
+ workspace: p_.required("workspace", workspace.1)?,
+ jobs: p_.optional("jobs", jobs.1)?,
+
+ log_file: p_.optional("--log-file", log_file)?,
+ verbose: verbose.len() as u32,
+ number: p_.required("--number", number)?,
+ data: data,
+ emoji: p_.optional("--emoji", emoji)?.is_some(),
+ })
+ }
+}
+impl RustAnalyzer {
+ const HELP_: &'static str = "\
+rust-analyzer
+ LSP server for rust.
+
+ARGS:
+ <workspace>
+
+ [jobs]
+ Number of concurrent jobs.
+
+OPTIONS:
+ --log-file <path>
+ Path to log file. By default, logs go to stderr.
+
+ -v, --verbose
+
+ -n, --number <n>
+
+ --data <value>
+
+ --emoji
+";
+}
diff --git a/vendor/xflags-macros/tests/it/src/help.rs b/vendor/xflags-macros/tests/it/src/help.rs
new file mode 100644
index 000000000..d552c1e63
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/src/help.rs
@@ -0,0 +1,25 @@
+xflags! {
+ /// Does stuff
+ ///
+ /// Helpful stuff.
+ cmd helpful
+ /// With an arg.
+ optional src: PathBuf
+ /// Another arg.
+ ///
+ /// This time, we provide some extra info about the
+ /// arg. Maybe some caveats, or what kinds of
+ /// values are accepted.
+ optional extra: String
+ {
+ /// And a switch.
+ required -s, --switch
+
+ /// And even a subcommand!
+ cmd sub {
+ /// With an optional flag. This has a really long
+ /// description which spans multiple lines.
+ optional -f, --flag
+ }
+ }
+}
diff --git a/vendor/xflags-macros/tests/it/src/repeated_pos.rs b/vendor/xflags-macros/tests/it/src/repeated_pos.rs
new file mode 100644
index 000000000..4106c65eb
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/src/repeated_pos.rs
@@ -0,0 +1,9 @@
+xflags! {
+ cmd RepeatedPos
+ required a: PathBuf
+ optional b: u32
+ optional c: OsString
+ repeated rest: OsString
+ {
+ }
+}
diff --git a/vendor/xflags-macros/tests/it/src/smoke.rs b/vendor/xflags-macros/tests/it/src/smoke.rs
new file mode 100644
index 000000000..ae303779e
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/src/smoke.rs
@@ -0,0 +1,15 @@
+xflags! {
+ /// LSP server for rust.
+ cmd rust-analyzer
+ required workspace: PathBuf
+ /// Number of concurrent jobs.
+ optional jobs: u32
+ {
+ /// Path to log file. By default, logs go to stderr.
+ optional --log-file path: PathBuf
+ repeated -v, --verbose
+ required -n, --number n: u32
+ repeated --data value: OsString
+ optional --emoji
+ }
+}
diff --git a/vendor/xflags-macros/tests/it/src/subcommands.rs b/vendor/xflags-macros/tests/it/src/subcommands.rs
new file mode 100644
index 000000000..70a0a5049
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/src/subcommands.rs
@@ -0,0 +1,20 @@
+xflags! {
+ cmd rust-analyzer {
+ repeated -v, --verbose
+
+ cmd server {
+ optional --dir path:PathBuf
+ default cmd launch {
+ optional --log
+ }
+ cmd watch {
+ }
+ }
+
+ cmd analysis-stats
+ required path: PathBuf
+ {
+ optional --parallel
+ }
+ }
+}
diff --git a/vendor/xflags-macros/tests/it/subcommands.rs b/vendor/xflags-macros/tests/it/subcommands.rs
new file mode 100644
index 000000000..7941a395d
--- /dev/null
+++ b/vendor/xflags-macros/tests/it/subcommands.rs
@@ -0,0 +1,225 @@
+#![allow(unused)]
+use std::{ffi::OsString, path::PathBuf};
+
+#[derive(Debug)]
+pub struct RustAnalyzer {
+ pub verbose: u32,
+ pub subcommand: RustAnalyzerCmd,
+}
+
+#[derive(Debug)]
+pub enum RustAnalyzerCmd {
+ Server(Server),
+ AnalysisStats(AnalysisStats),
+}
+
+#[derive(Debug)]
+pub struct Server {
+ pub dir: Option<PathBuf>,
+ pub subcommand: ServerCmd,
+}
+
+#[derive(Debug)]
+pub enum ServerCmd {
+ Launch(Launch),
+ Watch(Watch),
+}
+
+#[derive(Debug)]
+pub struct Launch {
+ pub log: bool,
+}
+
+#[derive(Debug)]
+pub struct Watch;
+
+#[derive(Debug)]
+pub struct AnalysisStats {
+ pub path: PathBuf,
+
+ pub parallel: bool,
+}
+
+impl RustAnalyzer {
+ pub const HELP: &'static str = Self::HELP_;
+
+ #[allow(dead_code)]
+ pub fn from_env() -> xflags::Result<Self> {
+ Self::from_env_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ Self::from_vec_(args)
+ }
+}
+
+impl RustAnalyzer {
+ fn from_env_() -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new_from_env();
+ Self::parse_(&mut p)
+ }
+ fn from_vec_(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ let mut p = xflags::rt::Parser::new(args);
+ Self::parse_(&mut p)
+ }
+}
+
+impl RustAnalyzer {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut verbose = Vec::new();
+
+ let mut sub_ = None;
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--verbose" | "-v" => verbose.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ match arg_.to_str().unwrap_or("") {
+ "server" => {
+ sub_ = Some(RustAnalyzerCmd::Server(Server::parse_(p_)?));
+ break;
+ }
+ "analysis-stats" => {
+ sub_ = Some(RustAnalyzerCmd::AnalysisStats(AnalysisStats::parse_(p_)?));
+ break;
+ }
+ _ => (),
+ }
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self { verbose: verbose.len() as u32, subcommand: p_.subcommand(sub_)? })
+ }
+}
+
+impl Server {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut dir = Vec::new();
+
+ let mut sub_ = None;
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--dir" => dir.push(p_.next_value(&flag_)?.into()),
+ _ => {
+ p_.push_back(Ok(flag_));
+ break;
+ }
+ },
+ Err(arg_) => {
+ match arg_.to_str().unwrap_or("") {
+ "watch" => {
+ sub_ = Some(ServerCmd::Watch(Watch::parse_(p_)?));
+ break;
+ }
+ _ => (),
+ }
+ p_.push_back(Err(arg_));
+ break;
+ }
+ }
+ }
+ if sub_.is_none() {
+ sub_ = Some(ServerCmd::Launch(Launch::parse_(p_)?));
+ }
+ Ok(Self { dir: p_.optional("--dir", dir)?, subcommand: p_.subcommand(sub_)? })
+ }
+}
+
+impl Launch {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut log = Vec::new();
+
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--log" => log.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self { log: p_.optional("--log", log)?.is_some() })
+ }
+}
+
+impl Watch {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self {})
+ }
+}
+
+impl AnalysisStats {
+ fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {
+ let mut parallel = Vec::new();
+
+ let mut path = (false, Vec::new());
+
+ while let Some(arg_) = p_.pop_flag() {
+ match arg_ {
+ Ok(flag_) => match flag_.as_str() {
+ "--parallel" => parallel.push(()),
+ _ => return Err(p_.unexpected_flag(&flag_)),
+ },
+ Err(arg_) => {
+ if let (done_ @ false, buf_) = &mut path {
+ buf_.push(arg_.into());
+ *done_ = true;
+ continue;
+ }
+ return Err(p_.unexpected_arg(arg_));
+ }
+ }
+ }
+ Ok(Self {
+ path: p_.required("path", path.1)?,
+
+ parallel: p_.optional("--parallel", parallel)?.is_some(),
+ })
+ }
+}
+impl RustAnalyzer {
+ const HELP_: &'static str = "\
+rust-analyzer
+
+OPTIONS:
+ -v, --verbose
+
+SUBCOMMANDS:
+
+rust-analyzer server
+
+ OPTIONS:
+ --dir <path>
+
+ --log
+
+
+rust-analyzer server watch
+
+
+rust-analyzer analysis-stats
+
+ ARGS:
+ <path>
+
+ OPTIONS:
+ --parallel
+";
+}