Comments (3)
For my usecase I ended up writing following derive proc macro:
#[proc_macro_derive(FlattenSubcommand)]
pub fn flatten_subcommand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as syn::DeriveInput);
let de = match ast.data {
syn::Data::Enum(v) => v,
_ => unreachable!(),
};
let name = &ast.ident;
// An enum variant like `<name>(<ty>)`
struct SubCommandVariant<'a> {
name: &'a syn::Ident,
ty: &'a syn::Type,
}
let variants: Vec<SubCommandVariant<'_>> = de
.variants
.iter()
.map(|variant| {
let name = &variant.ident;
let ty = match &variant.fields {
syn::Fields::Unnamed(field) => {
if field.unnamed.len() != 1 {
unreachable!()
}
&field.unnamed.first().unwrap().ty
}
_ => unreachable!(),
};
SubCommandVariant { name, ty }
})
.collect();
let variant_ty = variants.iter().map(|x| x.ty).collect::<Vec<_>>();
let variant_names = variants.iter().map(|x| x.name).collect::<Vec<_>>();
(quote! {
impl argh::FromArgs for #name {
fn from_args(command_name: &[&str], args: &[&str])
-> std::result::Result<Self, argh::EarlyExit>
{
let subcommand_name = if let Some(subcommand_name) = command_name.last() {
*subcommand_name
} else {
return Err(argh::EarlyExit::from("no subcommand name".to_owned()));
};
#(
if <#variant_ty as argh::SubCommands>::COMMANDS
.iter()
.find(|ci| ci.name.eq(subcommand_name))
.is_some()
{
return <#variant_ty as argh::FromArgs>::from_args(command_name, args)
.map(|v| Self::#variant_names(v));
}
)*
Err(argh::EarlyExit::from("no subcommand matched".to_owned()))
}
fn redact_arg_values(command_name: &[&str], args: &[&str]) -> std::result::Result<Vec<String>, argh::EarlyExit> {
let subcommand_name = if let Some(subcommand_name) = command_name.last() {
*subcommand_name
} else {
return Err(argh::EarlyExit::from("no subcommand name".to_owned()));
};
#(
if <#variant_ty as argh::SubCommands>::COMMANDS
.iter()
.find(|ci| ci.name.eq(subcommand_name))
.is_some()
{
return <#variant_ty as argh::FromArgs>::redact_arg_values(
command_name,
args,
);
}
)*
Err(argh::EarlyExit::from("no subcommand matched".to_owned()))
}
}
impl argh::SubCommands for #name {
const COMMANDS: &'static [&'static argh::CommandInfo] = {
const TOTAL_LEN: usize = #(<#variant_ty as argh::SubCommands>::COMMANDS.len())+*;
const COMMANDS: [&'static argh::CommandInfo; TOTAL_LEN] = {
let slices = &[#(<#variant_ty as argh::SubCommands>::COMMANDS,)*];
// Its not possible for slices[0][0] to be invalid
let mut output = [slices[0][0]; TOTAL_LEN];
let mut output_index = 0;
let mut which_slice = 0;
while which_slice < slices.len() {
let slice = &slices[which_slice];
let mut index_in_slice = 0;
while index_in_slice < slice.len() {
output[output_index] = slice[index_in_slice];
output_index += 1;
index_in_slice += 1;
}
which_slice += 1;
}
output
};
&COMMANDS
};
}
})
.into()
}
However I'm not sure if argh
is interested in this feature and if adding another derive macro is the way they would like to do that
from argh.
Duplicate of #15
from argh.
Duplicate of #15
#15 specifically talks about structs and i'm talking about flattening command enums here
from argh.
Related Issues (20)
- Unrecognized argument on raw identifier
- Process line breaks in description HOT 5
- [Optimisation] Stop re-parsing of commands for default sub commands
- Feature Request: choices HOT 5
- add an attribute on option argument to let `from_str_fn` can return `Vec<T>` or `Option<T>` as-is HOT 7
- how skip a field like #[serde(skip)] HOT 2
- Is it possible to get a `TopLevelCommand`'s `FieldAttrs` with the current exposed API?
- positional arguments made on field with reserved named (ref_) keep the trailing _ in help text
- Negative switches
- Subcommand with no additional arguments? HOT 1
- Impossible to include angle brackets in help text in a way that satisfies argh and rustdoc
- Change `FromArgs::from_args` to accept `AsRef<str>`
- Support something like --helpfull which displays help for all subcommands HOT 2
- `#[argh(description = "...")]` only supports literal HOT 2
- Single dash is parsed as unrecognized argument HOT 1
- Disable automatically generated help message
- mutual exclusive options/switches
- don't work as a cargo-subcommand
- Any ability to specify 'raw' greedy args or pass-through?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from argh.