diff options
Diffstat (limited to '')
29 files changed, 781 insertions, 564 deletions
diff --git a/vendor/xflags/LICENSE-APACHE b/vendor/rustc_tools_util/LICENSE-APACHE index 16fe87b06..0d62c3727 100644 --- a/vendor/xflags/LICENSE-APACHE +++ b/vendor/rustc_tools_util/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/xflags/LICENSE-MIT b/vendor/unicode-script/LICENSE-MIT index 31aa79387..20c28a2ce 100644 --- a/vendor/xflags/LICENSE-MIT +++ b/vendor/unicode-script/LICENSE-MIT @@ -1,23 +1,27 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +MIT License
+
+Copyright (c) 2019 Manish Goregaokar
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/vendor/xflags-macros/.cargo-checksum.json b/vendor/xflags-macros/.cargo-checksum.json index 98f5dfe97..fcc17d504 100644 --- a/vendor/xflags-macros/.cargo-checksum.json +++ b/vendor/xflags-macros/.cargo-checksum.json @@ -1 +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 +{"files":{"Cargo.toml":"dff52eef5d3a126eefb38eb66ecb194dd9ccd22a6e010df2b672d83264ac65a5","src/ast.rs":"1f3be0fa1c340dbd98f56fd1c10a43b081d4b9179b1666a3dd3f32d8082e452c","src/emit.rs":"03e692330adc927c1d7ea36faebec0df997dd2e6bfb4816661988078b59fdb0b","src/lib.rs":"ce57137b3db248a73b201a5add6b1ac5c80e431e2bc2947962d4b0b6f397a449","src/parse.rs":"15e860ce666b17228ae537b322c096a64696c83b620468a14112bab30c0432a8","src/update.rs":"133dbc864182808ea7679b815863117b51ccc59a0e43796321f9d5a2edcb3ed6","tests/data/help.rs":"a952a2f641fa6db5c7bd25d8480c1dff4f1146db21a0629e0506e4bc87c46d0c","tests/data/repeated_pos.rs":"60b7d6378583765ddda1be127d3f40aa34cf802c64008e9d3a456f5e20100290","tests/data/smoke.rs":"c5d626382c22f147deb0be9396bf4f8256d64618451f8c9b85002d3525c7fff6","tests/data/subcommands.rs":"c2020f895380c95ca6c97f124615ed64f838a6ee3d1bcce8842029c6cef55188","tests/it/help.rs":"d12aba6869f7d40984c9294591fb97fea7d8caf062969e5ae640ba21957a26c3","tests/it/main.rs":"08311bc06020d980867bd696040c6ddd4893700c7a181d4b9d256ef100945a46","tests/it/repeated_pos.rs":"f9acef062eda5ad43722cade6ebde4c0077174a0fae4e2c3c9555ad6dd490599","tests/it/smoke.rs":"3324c20e79258fca2dfe30a7312b5e192d09c104a30142b1362dd89e746d1306","tests/it/subcommands.rs":"900726a1309d753aa3b666c9b3051ec0b26cb1bf59c1d072704d8e497cfa18c4"},"package":"2afbd7f2039bb6cad2dd45f0c5dff49c0d4e26118398768b7a605524d4251809"}
\ No newline at end of file diff --git a/vendor/xflags-macros/Cargo.toml b/vendor/xflags-macros/Cargo.toml index a224e51a6..70b748f0a 100644 --- a/vendor/xflags-macros/Cargo.toml +++ b/vendor/xflags-macros/Cargo.toml @@ -10,16 +10,18 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" name = "xflags-macros" -version = "0.2.4" +version = "0.3.0" 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" +resolver = "1" [lib] proc-macro = true + [dev-dependencies.expect-test] version = "1" 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); diff --git a/vendor/xflags-macros/tests/it/src/help.rs b/vendor/xflags-macros/tests/data/help.rs index d552c1e63..f252e34fa 100644 --- a/vendor/xflags-macros/tests/it/src/help.rs +++ b/vendor/xflags-macros/tests/data/help.rs @@ -2,16 +2,17 @@ xflags! { /// Does stuff /// /// Helpful stuff. - cmd helpful + 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 diff --git a/vendor/xflags-macros/tests/it/src/repeated_pos.rs b/vendor/xflags-macros/tests/data/repeated_pos.rs index 4106c65eb..be7c552e0 100644 --- a/vendor/xflags-macros/tests/it/src/repeated_pos.rs +++ b/vendor/xflags-macros/tests/data/repeated_pos.rs @@ -1,9 +1,8 @@ xflags! { - cmd RepeatedPos + 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/data/smoke.rs index ae303779e..55da2d3ef 100644 --- a/vendor/xflags-macros/tests/it/src/smoke.rs +++ b/vendor/xflags-macros/tests/data/smoke.rs @@ -1,10 +1,9 @@ xflags! { /// LSP server for rust. - cmd rust-analyzer + 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 diff --git a/vendor/xflags-macros/tests/it/src/subcommands.rs b/vendor/xflags-macros/tests/data/subcommands.rs index 70a0a5049..45fab8232 100644 --- a/vendor/xflags-macros/tests/it/src/subcommands.rs +++ b/vendor/xflags-macros/tests/data/subcommands.rs @@ -11,9 +11,8 @@ xflags! { } } - cmd analysis-stats + cmd analysis-stats { required path: PathBuf - { optional --parallel } } diff --git a/vendor/xflags-macros/tests/it/help.rs b/vendor/xflags-macros/tests/it/help.rs index 36a966485..f17062988 100644 --- a/vendor/xflags-macros/tests/it/help.rs +++ b/vendor/xflags-macros/tests/it/help.rs @@ -21,7 +21,10 @@ pub struct Sub { } impl Helpful { - pub const HELP: &'static str = Self::HELP_; + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } #[allow(dead_code)] pub fn from_env() -> xflags::Result<Self> { @@ -35,6 +38,9 @@ impl Helpful { } impl Helpful { + fn from_env_or_exit_() -> Self { + Self::from_env_().unwrap_or_else(|err| err.exit()) + } fn from_env_() -> xflags::Result<Self> { let mut p = xflags::rt::Parser::new_from_env(); Self::parse_(&mut p) @@ -47,68 +53,51 @@ impl Helpful { impl Helpful { fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> { + #![allow(non_snake_case)] let mut switch = Vec::new(); - let mut src = (false, Vec::new()); let mut extra = (false, Vec::new()); + let mut sub__flag = Vec::new(); - let mut sub_ = None; + let mut state_ = 0u8; while let Some(arg_) = p_.pop_flag() { match arg_ { - Ok(flag_) => match flag_.as_str() { - "--switch" | "-s" => switch.push(()), + Ok(flag_) => match (state_, flag_.as_str()) { + (0 | 1, "--switch" | "-s") => switch.push(()), + (0 | 1, "--help" | "-h") => return Err(p_.help(Self::HELP_)), + (1, "--flag" | "-f") => sub__flag.push(()), _ => return Err(p_.unexpected_flag(&flag_)), }, - Err(arg_) => { - match arg_.to_str().unwrap_or("") { - "sub" => { - sub_ = Some(HelpfulCmd::Sub(Sub::parse_(p_)?)); - break; + Err(arg_) => match (state_, arg_.to_str().unwrap_or("")) { + (0, "sub") => state_ = 1, + (0, _) => { + if let (done_ @ false, buf_) = &mut src { + buf_.push(arg_.into()); + *done_ = true; + continue; } - _ => (), - } - 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; + 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_)); } - return Err(p_.unexpected_arg(arg_)); - } + _ => return Err(p_.unexpected_arg(arg_)), + }, } } - Ok(Self { + Ok(Helpful { + switch: p_.required("--switch", switch)?, src: p_.optional("src", src.1)?, extra: p_.optional("extra", extra.1)?, - - switch: p_.required("--switch", switch)?, - subcommand: p_.subcommand(sub_)?, + subcommand: match state_ { + 1 => HelpfulCmd::Sub(Sub { flag: p_.optional("--flag", sub__flag)?.is_some() }), + _ => return Err(p_.subcommand_required()), + }, }) } } - -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 @@ -131,6 +120,9 @@ OPTIONS: -s, --switch And a switch. + -h, --help + Prints help information. + SUBCOMMANDS: helpful sub diff --git a/vendor/xflags-macros/tests/it/main.rs b/vendor/xflags-macros/tests/it/main.rs index 1ce058814..7d77bda36 100644 --- a/vendor/xflags-macros/tests/it/main.rs +++ b/vendor/xflags-macros/tests/it/main.rs @@ -67,9 +67,10 @@ fn smoke() { "-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!["flag is required: `--number`"]); 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 92", expect!["flag is required: `workspace`"]); check( smoke::RustAnalyzer::from_vec, "-n lol", @@ -196,3 +197,36 @@ fn subcommands() { check(subcommands::RustAnalyzer::from_vec, "", expect![[r#"subcommand is required"#]]); } + +#[test] +fn subcommand_flag_inheritance() { + check( + subcommands::RustAnalyzer::from_vec, + "server watch --verbose --dir .", + expect![[r#" + RustAnalyzer { + verbose: 1, + subcommand: Server( + Server { + dir: Some( + ".", + ), + subcommand: Watch( + Watch, + ), + }, + ), + } + "#]], + ); + check( + subcommands::RustAnalyzer::from_vec, + "analysis-stats --verbose --dir .", + expect!["unexpected flag: `--dir`"], + ); + check( + subcommands::RustAnalyzer::from_vec, + "--dir . server", + expect!["unexpected flag: `--dir`"], + ); +} diff --git a/vendor/xflags-macros/tests/it/repeated_pos.rs b/vendor/xflags-macros/tests/it/repeated_pos.rs index 334af371d..b11b90717 100644 --- a/vendor/xflags-macros/tests/it/repeated_pos.rs +++ b/vendor/xflags-macros/tests/it/repeated_pos.rs @@ -10,7 +10,10 @@ pub struct RepeatedPos { } impl RepeatedPos { - pub const HELP: &'static str = Self::HELP_; + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } #[allow(dead_code)] pub fn from_env() -> xflags::Result<Self> { @@ -24,6 +27,9 @@ impl RepeatedPos { } impl RepeatedPos { + fn from_env_or_exit_() -> Self { + Self::from_env_().unwrap_or_else(|err| err.exit()) + } fn from_env_() -> xflags::Result<Self> { let mut p = xflags::rt::Parser::new_from_env(); Self::parse_(&mut p) @@ -36,41 +42,47 @@ impl RepeatedPos { impl RepeatedPos { fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> { + #![allow(non_snake_case)] let mut a = (false, Vec::new()); let mut b = (false, Vec::new()); let mut c = (false, Vec::new()); let mut rest = (false, Vec::new()); + let mut state_ = 0u8; while let Some(arg_) = p_.pop_flag() { match arg_ { - Ok(flag_) => match flag_.as_str() { + Ok(flag_) => match (state_, flag_.as_str()) { + (0, "--help" | "-h") => return Err(p_.help(Self::HELP_)), _ => 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; + Err(arg_) => match (state_, arg_.to_str().unwrap_or("")) { + (0, _) => { + 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_)); } - 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_)); - } + _ => return Err(p_.unexpected_arg(arg_)), + }, } } - Ok(Self { + Ok(RepeatedPos { a: p_.required("a", a.1)?, b: p_.optional("b", b.1)?, c: p_.optional("c", c.1)?, @@ -90,5 +102,9 @@ ARGS: [c] <rest>... + +OPTIONS: + -h, --help + Prints help information. "; } diff --git a/vendor/xflags-macros/tests/it/smoke.rs b/vendor/xflags-macros/tests/it/smoke.rs index e22c4f1f6..f2ebbb712 100644 --- a/vendor/xflags-macros/tests/it/smoke.rs +++ b/vendor/xflags-macros/tests/it/smoke.rs @@ -14,7 +14,10 @@ pub struct RustAnalyzer { } impl RustAnalyzer { - pub const HELP: &'static str = Self::HELP_; + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } #[allow(dead_code)] pub fn from_env() -> xflags::Result<Self> { @@ -28,6 +31,9 @@ impl RustAnalyzer { } impl RustAnalyzer { + fn from_env_or_exit_() -> Self { + Self::from_env_().unwrap_or_else(|err| err.exit()) + } fn from_env_() -> xflags::Result<Self> { let mut p = xflags::rt::Parser::new_from_env(); Self::parse_(&mut p) @@ -40,49 +46,53 @@ impl RustAnalyzer { impl RustAnalyzer { fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> { + #![allow(non_snake_case)] 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()); + let mut state_ = 0u8; 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(()), + Ok(flag_) => match (state_, flag_.as_str()) { + (0, "--log-file") => log_file.push(p_.next_value(&flag_)?.into()), + (0, "--verbose" | "-v") => verbose.push(()), + (0, "--number" | "-n") => number.push(p_.next_value_from_str::<u32>(&flag_)?), + (0, "--data") => data.push(p_.next_value(&flag_)?.into()), + (0, "--emoji") => emoji.push(()), + (0, "--help" | "-h") => return Err(p_.help(Self::HELP_)), _ => 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; + Err(arg_) => match (state_, arg_.to_str().unwrap_or("")) { + (0, _) => { + 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_)); } - return Err(p_.unexpected_arg(arg_)); - } + _ => return Err(p_.unexpected_arg(arg_)), + }, } } - Ok(Self { - workspace: p_.required("workspace", workspace.1)?, - jobs: p_.optional("jobs", jobs.1)?, - + Ok(RustAnalyzer { 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(), + workspace: p_.required("workspace", workspace.1)?, + jobs: p_.optional("jobs", jobs.1)?, }) } } @@ -108,5 +118,8 @@ OPTIONS: --data <value> --emoji + + -h, --help + Prints help information. "; } diff --git a/vendor/xflags-macros/tests/it/subcommands.rs b/vendor/xflags-macros/tests/it/subcommands.rs index 7941a395d..4d0a64923 100644 --- a/vendor/xflags-macros/tests/it/subcommands.rs +++ b/vendor/xflags-macros/tests/it/subcommands.rs @@ -41,7 +41,10 @@ pub struct AnalysisStats { } impl RustAnalyzer { - pub const HELP: &'static str = Self::HELP_; + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } #[allow(dead_code)] pub fn from_env() -> xflags::Result<Self> { @@ -55,6 +58,9 @@ impl RustAnalyzer { } impl RustAnalyzer { + fn from_env_or_exit_() -> Self { + Self::from_env_().unwrap_or_else(|err| err.exit()) + } fn from_env_() -> xflags::Result<Self> { let mut p = xflags::rt::Parser::new_from_env(); Self::parse_(&mut p) @@ -67,130 +73,71 @@ impl RustAnalyzer { impl RustAnalyzer { fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> { + #![allow(non_snake_case)] let mut verbose = Vec::new(); + let mut server__dir = Vec::new(); + let mut server__launch__log = Vec::new(); + let mut analysis_stats__parallel = Vec::new(); + let mut analysis_stats__path = (false, Vec::new()); - let mut sub_ = None; + let mut state_ = 0u8; while let Some(arg_) = p_.pop_flag() { match arg_ { - Ok(flag_) => match flag_.as_str() { - "--verbose" | "-v" => verbose.push(()), + Ok(flag_) => match (state_, flag_.as_str()) { + (0 | 1 | 2 | 3 | 4, "--verbose" | "-v") => verbose.push(()), + (0 | 1 | 2 | 3 | 4, "--help" | "-h") => return Err(p_.help(Self::HELP_)), + (1 | 2 | 3, "--dir") => server__dir.push(p_.next_value(&flag_)?.into()), + (1, _) => { + p_.push_back(Ok(flag_)); + state_ = 2; + } + (2, "--log") => server__launch__log.push(()), + (4, "--parallel") => analysis_stats__parallel.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; - } - _ => (), + Err(arg_) => match (state_, arg_.to_str().unwrap_or("")) { + (0, "server") => state_ = 1, + (0, "analysis-stats") => state_ = 4, + (0, _) => { + return Err(p_.unexpected_arg(arg_)); } - 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; + (1, "watch") => state_ = 3, + (1, _) => { + p_.push_back(Err(arg_)); + state_ = 2; } - }, - Err(arg_) => { - match arg_.to_str().unwrap_or("") { - "watch" => { - sub_ = Some(ServerCmd::Watch(Watch::parse_(p_)?)); - break; + (4, _) => { + if let (done_ @ false, buf_) = &mut analysis_stats__path { + buf_.push(arg_.into()); + *done_ = true; + continue; } - _ => (), + return Err(p_.unexpected_arg(arg_)); } - 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_)), + _ => return Err(p_.unexpected_arg(arg_)), }, - 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(), + state_ = if state_ == 1 { 2 } else { state_ }; + Ok(RustAnalyzer { + verbose: verbose.len() as u32, + subcommand: match state_ { + 2 | 3 => RustAnalyzerCmd::Server(Server { + dir: p_.optional("--dir", server__dir)?, + subcommand: match state_ { + 2 => ServerCmd::Launch(Launch { + log: p_.optional("--log", server__launch__log)?.is_some(), + }), + 3 => ServerCmd::Watch(Watch {}), + _ => return Err(p_.subcommand_required()), + }, + }), + 4 => RustAnalyzerCmd::AnalysisStats(AnalysisStats { + parallel: p_.optional("--parallel", analysis_stats__parallel)?.is_some(), + path: p_.required("path", analysis_stats__path.1)?, + }), + _ => return Err(p_.subcommand_required()), + }, }) } } @@ -201,6 +148,9 @@ rust-analyzer OPTIONS: -v, --verbose + -h, --help + Prints help information. + SUBCOMMANDS: rust-analyzer server diff --git a/vendor/xflags/.cargo-checksum.json b/vendor/xflags/.cargo-checksum.json index c5fd48200..1fa4dd9e5 100644 --- a/vendor/xflags/.cargo-checksum.json +++ b/vendor/xflags/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"76747bdf0a7c1643e62a7c8db6e65572906908415ba87daaa7444917cdf1d35b","Cargo.toml":"32dc60919f757b61d4a6602da24bdd59b77b76f284a1b55712a78e1159e2d249","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"d34d354ca65973ca9120ed16af11e188c91af2ede6894e0e842c10c874780f7b","examples/hello-generated.rs":"ac2d4864e238e664fb1b18db66aed75ac5a425877bc00729fed1135fb410b969","examples/hello.rs":"c1a5656bee529c3ef96a3afc3531e02bc9d5d309e1bcd60b76e6758fb4c2a95d","examples/longer.rs":"18ad0d00215fdf245830a581df32fbf9533f5ab47e736659e598b4ecd2d96e1a","examples/non-utf8.rs":"a9d4a03adb05c8b46c3251647b8dbf4e860277db995e14192cd8d445f3434186","src/lib.rs":"21843e64aba8010dcc877418927e5b0ad3615ff6e18020285849bbf4391f4ed0","src/rt.rs":"c3cb2d6c80ec9c0586a6e64441cee4c237d2fb420891dec2acb96d5b0c7d6937"},"package":"3f14fe1ed41a5a2b5ef3f565586c4a8a559ee55d3953faab360a771135bdee00"}
\ No newline at end of file +{"files":{"Cargo.lock":"bff2ffbef3ca253bcde1624bfc2c2afd244ca39b44e7dd3f4b463873904129c7","Cargo.toml":"faa866c2c32635dfa6d600103595777c330520df5e73aa0461d6f34eb21bbd9a","examples/hello-generated.rs":"3bf7922435ae84e2b8ae022aba1d693fc4e523b354d3141cc62a183864531f31","examples/hello.rs":"235426d1a69eeba790d1e4fc2ac4fa8f30ca16952edb2a026cf6f4b8c78d6fec","examples/immediate-mode.rs":"dcae135769b6104801de7b91fd220fb28ddae097ba7b0474fe32c28a90c4dfef","examples/longer.rs":"b55684fdde16a8462e1c1afaa9f30e75e3472abfcabe950403733c767dd7de96","examples/non-utf8.rs":"23b563180d1d616f81e224890192799a06fdbc21f3ccb7e4234ca51b3c467a9a","src/lib.rs":"780d7be977abefaa4c7229733a916d88480c9228ddb0f9bc2a25aaa626f7f7d7","src/rt.rs":"b80e6b1e180b0bd0fd523a6bc74d6bba40da7af461de37c33c3735bfb3c66254"},"package":"cbf19f5031a1a812e96fede16f8161218883079946cea87619d3613db1efd268"}
\ No newline at end of file diff --git a/vendor/xflags/Cargo.lock b/vendor/xflags/Cargo.lock index 37ff98bb9..e2020eb16 100644 --- a/vendor/xflags/Cargo.lock +++ b/vendor/xflags/Cargo.lock @@ -4,13 +4,13 @@ version = 3 [[package]] name = "xflags" -version = "0.2.4" +version = "0.3.0" dependencies = [ "xflags-macros", ] [[package]] name = "xflags-macros" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022" +checksum = "2afbd7f2039bb6cad2dd45f0c5dff49c0d4e26118398768b7a605524d4251809" diff --git a/vendor/xflags/Cargo.toml b/vendor/xflags/Cargo.toml index 542701052..091002385 100644 --- a/vendor/xflags/Cargo.toml +++ b/vendor/xflags/Cargo.toml @@ -10,14 +10,15 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" name = "xflags" -version = "0.2.4" +version = "0.3.0" authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"] -exclude = [".github/", "bors.toml", "rustfmt.toml"] description = "Moderately simple command line arguments parser." categories = ["command-line-interface"] license = "MIT OR Apache-2.0" repository = "https://github.com/matklad/xflags" +resolver = "1" + [dependencies.xflags-macros] -version = "=0.2.4" +version = "=0.3.0" diff --git a/vendor/xflags/README.md b/vendor/xflags/README.md deleted file mode 100644 index bfebfb0c8..000000000 --- a/vendor/xflags/README.md +++ /dev/null @@ -1,49 +0,0 @@ -[![API reference](https://docs.rs/once_cell/badge.svg)](https://docs.rs/xflags/) - -# xflags - -Moderately simple command line arguments parsing: - -```rust -mod flags { - use std::path::PathBuf; - - xflags::xflags! { - src "./examples/basic.rs" - - cmd my-command - required path: PathBuf - { - optional -v, --verbose - } - } - - // generated start - // The following code is generated by `xflags` macro. - // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. - #[derive(Debug)] - pub struct MyCommand { - pub path: PathBuf, - - pub verbose: bool, - } - - impl MyCommand { - pub const HELP: &'static str = Self::HELP_; - - pub fn from_env() -> xflags::Result<Self> { - Self::from_env_() - } - - pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> { - Self::from_vec_(args) - } - } - // generated end -} - -fn main() { - let flags = flags::MyCommand::from_env(); - println!("{:#?}", flags); -} -``` diff --git a/vendor/xflags/examples/hello-generated.rs b/vendor/xflags/examples/hello-generated.rs index b82884859..cb74acbd7 100644 --- a/vendor/xflags/examples/hello-generated.rs +++ b/vendor/xflags/examples/hello-generated.rs @@ -5,10 +5,10 @@ mod flags { src "./examples/hello-generated.rs" /// Prints a greeting. - cmd hello + cmd hello { /// Whom to greet. required name: String - { + /// Use non-ascii symbols in the output. optional -e, --emoji } @@ -25,12 +25,12 @@ mod flags { } impl Hello { - 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) } @@ -44,9 +44,6 @@ fn main() { let bang = if flags.emoji { "❣️" } else { "!" }; println!("Hello {}{}", flags.name, bang); } - Err(err) => { - eprintln!("{}\n\n{}", err, flags::Hello::HELP); - std::process::exit(1) - } + Err(err) => err.exit(), } } diff --git a/vendor/xflags/examples/hello.rs b/vendor/xflags/examples/hello.rs index 98a19099f..91dc3fd8a 100644 --- a/vendor/xflags/examples/hello.rs +++ b/vendor/xflags/examples/hello.rs @@ -1,8 +1,7 @@ mod flags { xflags::xflags! { - cmd hello + cmd hello { required name: String - { optional -e, --emoji } } diff --git a/vendor/xflags/examples/immediate-mode.rs b/vendor/xflags/examples/immediate-mode.rs new file mode 100644 index 000000000..ee3baa3de --- /dev/null +++ b/vendor/xflags/examples/immediate-mode.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; + +fn main() { + let flags = xflags::parse_or_exit! { + /// Remove directories and their contents recursively. + optional -r,--recursive + /// File or directory to remove + required path: PathBuf + }; + + println!( + "removing {}{}", + flags.path.display(), + if flags.recursive { "recursively" } else { "" }, + ) +} diff --git a/vendor/xflags/examples/longer.rs b/vendor/xflags/examples/longer.rs index 7ea9e18d5..199cca042 100644 --- a/vendor/xflags/examples/longer.rs +++ b/vendor/xflags/examples/longer.rs @@ -24,10 +24,9 @@ mod flags { } /// Benchmark specific analysis operation - cmd analysis-bench + cmd analysis-bench { /// Directory with Cargo.toml optional path: PathBuf - { /// Compute syntax highlighting for this file required --highlight path: PathBuf /// Compute highlighting for this line @@ -72,12 +71,12 @@ mod flags { } 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) } diff --git a/vendor/xflags/examples/non-utf8.rs b/vendor/xflags/examples/non-utf8.rs index d355b24f6..28b1e98be 100644 --- a/vendor/xflags/examples/non-utf8.rs +++ b/vendor/xflags/examples/non-utf8.rs @@ -4,11 +4,10 @@ mod flags { use std::{ffi::OsString, path::PathBuf}; xflags::xflags! { - cmd Cmd + cmd Cmd { required a: OsString required b: PathBuf required c: String - { } } } diff --git a/vendor/xflags/src/lib.rs b/vendor/xflags/src/lib.rs index ae347f511..0eda9dec4 100644 --- a/vendor/xflags/src/lib.rs +++ b/vendor/xflags/src/lib.rs @@ -1,12 +1,65 @@ -//! This crates provides a procedural macro for parsing command line arguments. +//! `xflags` provides a procedural macro for parsing command line arguments. //! //! It is intended for use in development tools, so it emphasizes fast compile //! times and convenience at the expense of features. //! -//! If you need something more fancy, consider using -//! [`clap`](https://docs.rs/clap/2.33.3/clap/) instead. +//! Rough decision tree for picking an argument parsing library: //! -//! ## Example +//! * if you need all of the features and don't care about minimalism, use +//! [clap](https://github.com/clap-rs/clap) +//! * if you want to be maximally minimal, need only basic features (eg, no help +//! generation), and want to be pedantically correct, use +//! [lexopt](https://github.com/blyxxyz/lexopt) +//! * if you want to get things done fast (eg, you want auto help, but not at +//! the cost of waiting for syn to compile), consider this crate. +//! +//! The secret sauce of xflags is that it is the opposite of a derive macro. +//! Rather than generating a command line grammar from a Rust struct, `xflags` +//! generates Rust structs based on input grammar. The grammar definition is +//! both shorter and simpler to write, and is lighter on compile times. +//! +//! Here's a complete example of `parse_or_exit!` macro which parses arguments +//! into an "anonymous" struct: +//! +//! ```no_run +//! use std::path::PathBuf; +//! +//! fn main() { +//! let flags = xflags::parse_or_exit! { +//! /// Remove directories and their contents recursively. +//! optional -r,--recursive +//! /// File or directory to remove +//! required path: PathBuf +//! }; +//! +//! println!( +//! "removing {}{}", +//! flags.path.display(), +//! if flags.recursive { "recursively" } else { "" }, +//! ) +//! } +//! ``` +//! +//! The above program, when run with `--help` argument, generates the following +//! help: +//! +//! ```text +//! ARGS: +//! <path> +//! File or directory to remove +//! +//! OPTIONS: +//! -r, --recursive +//! Remove directories and their contents recursively. +//! +//! -h, --help +//! Prints help information. +//! ``` +//! +//! For larger programs, you'd typically want to use `xflags!` macro, which +//! generates _named_ structs for you. Unlike a typical macro, `xflags` writes +//! generated code into the source file, to make it easy to understand the rust +//! types at a glance. //! //! ``` //! mod flags { @@ -15,9 +68,8 @@ //! xflags::xflags! { //! src "./examples/basic.rs" //! -//! cmd my-command +//! cmd my-command { //! required path: PathBuf -//! { //! optional -v, --verbose //! } //! } @@ -28,17 +80,16 @@ //! #[derive(Debug)] //! pub struct MyCommand { //! pub path: PathBuf, -//! //! pub verbose: bool, //! } //! //! impl MyCommand { -//! pub const HELP: &'static str = Self::HELP_; -//! +//! pub fn from_env_or_exit() -> Self { +//! Self::from_env_or_exit_() +//! } //! pub fn from_env() -> xflags::Result<Self> { //! Self::from_env_() //! } -//! //! pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> { //! Self::from_vec_(args) //! } @@ -52,15 +103,15 @@ //! } //! ``` //! -//! To make the macro less opaque, `xflag` can generate `struct` describing the -//! CLI in-place. To disable this behavior, omit the `src` attribute. +//! If you'd rather use a typical proc-macro which generates hidden code, just +//! omit the src attribute. //! //! xflags correctly handles non-utf8 arguments. //! //! ## Syntax Reference //! -//! The **cmd** keyword introduces a command that accepts positional arguments -//! and switches. +//! The `xflags!` macro uses **cmd** keyword to introduce a command or +//! subcommand that accepts positional arguments and switches. //! //! ``` //! xflags::xflags! { @@ -75,9 +126,9 @@ //! ``` //! xflags::xflags! { //! cmd switches { -//! optional -h, --help -//! repeated --verbose +//! optional -q,--quiet //! required --pass-me +//! repeated --verbose //! } //! } //! ``` @@ -98,20 +149,21 @@ //! } //! ``` //! -//! Positional arguments are specified before the opening curly brace: +//! Arguments without `--` in then are are positional. //! //! ``` //! use std::{path::PathBuf, ffi::OsString}; //! //! xflags::xflags! { -//! cmd positional-arguments +//! cmd positional-arguments { //! required program: PathBuf //! repeated args: OsString -//! { } +//! } //! } //! ``` //! -//! Nesting **cmd** is allowed: +//! Nesting **cmd** is allowed. `xflag` automatically generates boilerplate +//! enums for subcommands: //! //! ```ignore //! xflags::xflags! { @@ -148,12 +200,12 @@ //! } //! //! impl App { -//! pub const HELP: &'static str = Self::HELP_; -//! +//! pub fn from_env_or_exit() -> Self { +//! Self::from_env_or_exit_() +//! } //! pub fn from_env() -> xflags::Result<Self> { //! Self::from_env_() //! } -//! //! pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> { //! Self::from_vec_(args) //! } @@ -161,6 +213,9 @@ //! // generated end //! ``` //! +//! Switches are always "inherited". Both `app -v foo` and `app foo -v` produce +//! the same result. +//! //! To make subcommand name optional use the **default** keyword to mark a //! subcommand to select if no subcommand name is passed. The name of the //! default subcommand affects only the name of the generated Rust struct, it @@ -176,8 +231,8 @@ //! } //! ``` //! -//! Commands, arguments, and switches can documented. Doc comments become a part -//! of generated help: +//! Commands, arguments, and switches can be documented. Doc comments become a +//! part of generated help: //! //! ``` //! mod flags { @@ -185,14 +240,11 @@ //! //! xflags::xflags! { //! /// Run basic system diagnostics. -//! cmd healthck +//! cmd healthck { //! /// Optional configuration file. //! optional config: PathBuf -//! { //! /// Verbosity level, can be repeated multiple times. //! repeated -v, --verbose -//! /// Print the help message. -//! optional -h, --help //! } //! } //! } @@ -200,24 +252,18 @@ //! fn main() { //! match flags::Healthck::from_env() { //! Ok(flags) => { -//! if flags.help { -//! println!("{}", flags::Healthck::HELP); -//! return; -//! } //! run_checks(flags.config, flags.verbose); //! } -//! Err(err) => { -//! eprintln!("{}", err); -//! } +//! Err(err) => err.exit() //! } //! } //! //! # fn run_checks(_config: Option<std::path::PathBuf>, _verbosity: u32) {} //! ``` //! -//! The **src** keyword controlls how the code generation works. If it is -//! absent, `xflags` acts as a typical procedure macro, which generates a bunch -//! of structs and impls. +//! The **src** keyword controls how the code generation works. If it is absent, +//! `xflags` acts as a typical procedure macro, which generates a bunch of +//! structs and impls. //! //! If the **src** keyword is present, it should specify the path to the file //! with `xflags!` invocation. The path should be relative to the directory with @@ -226,7 +272,7 @@ //! directly to the specified file. //! //! By convention, `xflag!` macro should be invoked from the `flags` submodule. -//! The `flags::` preffix should be used to refer to command names. Additional +//! The `flags::` prefix should be used to refer to command names. Additional //! validation logic can go to the `flags` module: //! //! ``` @@ -250,21 +296,43 @@ //! } //! } //! ``` +//! +//! The `parse_or_exit!` macro is a syntactic sure for `xflags!`, which +//! immediately parses the argument, exiting the process if needed. +//! `parse_or_exit` only supports single top-level command and doesn't need the +//! `cmd` keyword. +//! +//! ## Limitations +//! +//! `xflags` follows +//! [Fuchsia](https://fuchsia.dev/fuchsia-src/development/api/cli#command_line_arguments) +//! conventions for command line arguments. GNU conventions such as grouping +//! short-flags (`-xyz`) or gluing short flag and a value `(-fVAL)` are not +//! supported. +//! +//! `xflags` requires the command line interface to be fully static. It's +//! impossible to include additional flags at runtime. +//! +//! Implementation is not fully robust, there might be some residual bugs in +//! edge cases. use std::fmt; /// Generates a parser for command line arguments from a DSL. /// /// See the module-level for detailed syntax specification. -pub use xflags_macros::xflags; +pub use xflags_macros::{parse_or_exit, xflags}; pub type Result<T, E = Error> = std::result::Result<T, E>; -/// This type represents an error that can occur during command line argument -/// parsing. +/// An error occurred when parssing command line arguments. +/// +/// Either the command line was syntactically invalid, or `--help` was +/// explicitly requested. #[derive(Debug)] pub struct Error { msg: String, + help: bool, } impl fmt::Display for Error { @@ -280,7 +348,23 @@ impl Error { /// /// Use this to report custom validation errors. pub fn new(message: impl Into<String>) -> Error { - Error { msg: message.into() } + Error { msg: message.into(), help: false } + } + + /// Error that carries `--help` message. + pub fn is_help(&self) -> bool { + self.help + } + + /// Prints the error and exists the process. + pub fn exit(self) -> ! { + if self.is_help() { + println!("{self}"); + std::process::exit(0) + } else { + eprintln!("{self}"); + std::process::exit(2) + } } } diff --git a/vendor/xflags/src/rt.rs b/vendor/xflags/src/rt.rs index 3c33196af..988e6cc23 100644 --- a/vendor/xflags/src/rt.rs +++ b/vendor/xflags/src/rt.rs @@ -4,7 +4,7 @@ use crate::{Error, Result}; macro_rules! format_err { ($($tt:tt)*) => { - Error { msg: format!($($tt)*) } + Error { msg: format!($($tt)*), help: false } }; } @@ -94,6 +94,14 @@ impl Parser { format_err!("unexpected argument: {:?}", arg) } + pub fn subcommand_required(&self) -> Error { + format_err!("subcommand is required") + } + + pub fn help(&self, help: &'static str) -> Error { + Error { msg: help.to_string(), help: true } + } + 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) @@ -107,8 +115,4 @@ impl Parser { } vals.pop().ok_or_else(|| format_err!("flag is required: `{}`", flag)) } - - pub fn subcommand<T>(&self, cmd: Option<T>) -> Result<T> { - cmd.ok_or_else(|| format_err!("subcommand is required")) - } } |