Allow separate derives, but do not give examples

This commit is contained in:
Pavan Kumar Sunkara 2020-05-15 09:51:36 +02:00
parent ddaa371d0c
commit fe52d77f4e
12 changed files with 158 additions and 59 deletions

View file

@ -10,16 +10,37 @@
use crate::{
attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
dummies,
utils::Sp,
};
use proc_macro2::{Span, TokenStream};
use proc_macro_error::abort_call_site;
use quote::quote;
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, DataEnum, Ident, Variant,
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataEnum, DeriveInput,
Fields, Ident, Variant,
};
pub fn derive_arg_enum(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::arg_enum(ident);
match input.data {
Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
_ => abort_call_site!("`#[derive(ArgEnum)]` only supports enums"),
}
}
pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
if !e.variants.iter().all(|v| match v.fields {
Fields::Unit => true,
_ => false,
}) {
return quote!();
};
let attrs = Attrs::from_struct(
Span::call_site(),
attrs,

View file

@ -13,40 +13,37 @@
// MIT/Apache 2.0 license.
use crate::{
derives::{arg_enum, from_argmatches, into_app, subcommand},
derives::{arg_enum, from_arg_matches, into_app, subcommand},
dummies,
};
use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site;
use quote::quote;
use syn::{self, punctuated, token, Attribute, DataEnum, DeriveInput, Field, Ident};
use syn::{
self, punctuated::Punctuated, token::Comma, Attribute, Data, DataEnum, DataStruct, DeriveInput,
Field, Fields, Ident,
};
pub fn derive_clap(input: &DeriveInput) -> TokenStream {
use syn::Data::*;
let ident = &input.ident;
match input.data {
Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => {
dummies::clap_struct(ident);
gen_for_struct(ident, &fields.named, &input.attrs)
}
Struct(syn::DataStruct {
fields: syn::Fields::Unit,
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => {
dummies::clap_struct(ident);
gen_for_struct(
ident,
&punctuated::Punctuated::<Field, token::Comma>::new(),
&input.attrs,
)
gen_for_struct(ident, &Punctuated::<Field, Comma>::new(), &input.attrs)
}
Enum(ref e) => {
Data::Enum(ref e) => {
dummies::clap_enum(ident);
gen_for_enum(ident, &input.attrs, e)
}
@ -56,11 +53,11 @@ pub fn derive_clap(input: &DeriveInput) -> TokenStream {
fn gen_for_struct(
name: &Ident,
fields: &punctuated::Punctuated<Field, token::Comma>,
fields: &Punctuated<Field, Comma>,
attrs: &[Attribute],
) -> TokenStream {
let (into_app, attrs) = into_app::gen_for_struct(name, fields, attrs);
let from_arg_matches = from_argmatches::gen_for_struct(name, fields, &attrs);
let from_arg_matches = from_arg_matches::gen_for_struct(name, fields, &attrs);
quote! {
impl ::clap::Clap for #name {}
@ -72,20 +69,12 @@ fn gen_for_struct(
fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
let into_app = into_app::gen_for_enum(name);
let from_arg_matches = from_argmatches::gen_for_enum(name);
let from_arg_matches = from_arg_matches::gen_for_enum(name);
let subcommand = subcommand::gen_for_enum(name, attrs, e);
let arg_enum = if e.variants.iter().all(|v| match v.fields {
syn::Fields::Unit => true,
_ => false,
}) {
arg_enum::gen_for_enum(name, attrs, e)
} else {
quote!()
};
let arg_enum = arg_enum::gen_for_enum(name, attrs, e);
quote! {
impl ::clap::Clap for #name { }
impl ::clap::Clap for #name {}
#into_app
#from_arg_matches

View file

@ -14,7 +14,7 @@
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use quote::{quote, quote_spanned};
use syn::{punctuated::Punctuated, spanned::Spanned, Field, Ident, Token, Type};
use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Field, Ident, Type};
use crate::{
attrs::{Attrs, Kind, ParserKind},
@ -23,7 +23,7 @@ use crate::{
pub fn gen_for_struct(
struct_name: &Ident,
fields: &Punctuated<Field, Token![,]>,
fields: &Punctuated<Field, Comma>,
parent_attribute: &Attrs,
) -> TokenStream {
let constructor = gen_constructor(fields, parent_attribute);
@ -81,10 +81,7 @@ fn gen_arg_enum_parse(ty: &Type, attrs: &Attrs) -> TokenStream {
}
}
pub fn gen_constructor(
fields: &Punctuated<Field, Token![,]>,
parent_attribute: &Attrs,
) -> TokenStream {
pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
let fields = fields.iter().map(|field| {
let attrs = Attrs::from_field(
field,

View file

@ -14,19 +14,42 @@
use std::env;
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::{quote, quote_spanned};
use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Field, Ident, Token, Type};
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataStruct,
DeriveInput, Field, Fields, Ident, Type,
};
use crate::{
attrs::{Attrs, GenOutput, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING},
dummies,
utils::{sub_type, subty_if_name, Sp, Ty},
};
pub fn derive_into_app(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::into_app(ident);
match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => gen_for_struct(ident, &fields.named, &input.attrs).0,
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => gen_for_struct(ident, &Punctuated::<Field, Comma>::new(), &input.attrs).0,
Data::Enum(_) => gen_for_enum(ident),
_ => abort_call_site!("`#[derive(IntoApp)]` only supports non-tuple structs and enums"),
}
}
pub fn gen_for_struct(
struct_name: &Ident,
fields: &Punctuated<Field, Token![,]>,
fields: &Punctuated<Field, Comma>,
attrs: &[Attribute],
) -> GenOutput {
let (into_app, attrs) = gen_into_app_fn(attrs);
@ -88,7 +111,7 @@ fn gen_into_app_fn(attrs: &[Attribute]) -> GenOutput {
let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
let attrs = Attrs::from_struct(
proc_macro2::Span::call_site(),
Span::call_site(),
attrs,
Name::Assigned(quote!(#app_name)),
Sp::call_site(DEFAULT_CASING),
@ -105,11 +128,8 @@ fn gen_into_app_fn(attrs: &[Attribute]) -> GenOutput {
(tokens, attrs)
}
fn gen_augment_clap_fn(
fields: &Punctuated<Field, Token![,]>,
parent_attribute: &Attrs,
) -> TokenStream {
let app_var = Ident::new("app", proc_macro2::Span::call_site());
fn gen_augment_clap_fn(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
let app_var = Ident::new("app", Span::call_site());
let augmentation = gen_app_augmentation(fields, &app_var, parent_attribute);
quote! {
fn augment_clap<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> {
@ -127,7 +147,7 @@ fn gen_arg_enum_possible_values(ty: &Type) -> TokenStream {
/// Generate a block of code to add arguments/subcommands corresponding to
/// the `fields` to an app.
pub fn gen_app_augmentation(
fields: &Punctuated<Field, Token![,]>,
fields: &Punctuated<Field, Comma>,
app_var: &Ident,
parent_attribute: &Attrs,
) -> TokenStream {

View file

@ -13,8 +13,12 @@
// MIT/Apache 2.0 license.
mod arg_enum;
mod clap;
mod from_argmatches;
mod from_arg_matches;
mod into_app;
mod subcommand;
pub use self::clap::derive_clap;
pub use arg_enum::derive_arg_enum;
// pub use from_arg_matches::derive_from_arg_matches;
pub use into_app::derive_into_app;
pub use subcommand::derive_subcommand;

View file

@ -1,6 +1,7 @@
use crate::{
attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
derives::{from_argmatches, into_app},
derives::{from_arg_matches, into_app},
dummies,
utils::{is_simple_ty, subty_if_name, Sp},
};
@ -8,9 +9,21 @@ use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::{quote, quote_spanned};
use syn::{
punctuated::Punctuated, spanned::Spanned, Attribute, DataEnum, FieldsUnnamed, Token, Variant,
punctuated::Punctuated, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput,
FieldsUnnamed, Token, Variant,
};
pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::subcommand(ident);
match input.data {
Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
}
}
pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
let attrs = Attrs::from_struct(
Span::call_site(),
@ -209,7 +222,7 @@ fn gen_from_subcommand(
let sub_name = attrs.cased_name();
let variant_name = &variant.ident;
let constructor_block = match variant.fields {
Named(ref fields) => from_argmatches::gen_constructor(&fields.named, &attrs),
Named(ref fields) => from_arg_matches::gen_constructor(&fields.named, &attrs),
Unit => quote!(),
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0];

View file

@ -18,7 +18,9 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::{parse_macro_input, DeriveInput};
mod attrs;
mod derives;
@ -26,17 +28,42 @@ mod dummies;
mod parse;
mod utils;
// /// It is required to have this seperate and specificly defined.
// #[proc_macro_derive(ArgEnum, attributes(case_sensitive))]
// pub fn arg_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// let input: syn::DeriveInput = syn::parse(input).unwrap();
// derives::derive_arg_enum(&input).into()
// }
/// Generates the `ArgEnum` impl.
#[proc_macro_derive(ArgEnum, attributes(clap))]
#[proc_macro_error]
pub fn arg_enum(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_arg_enum(&input).into()
}
/// Generates the `Clap` impl.
#[proc_macro_derive(Clap, attributes(clap))]
#[proc_macro_error]
pub fn clap(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: syn::DeriveInput = syn::parse_macro_input!(input);
pub fn clap(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_clap(&input).into()
}
// /// Generates the `FromArgMatches` impl.
// #[proc_macro_derive(FromArgMatches, attributes(clap))]
// #[proc_macro_error]
// pub fn from_arg_matches(input: TokenStream) -> TokenStream {
// let input: DeriveInput = parse_macro_input!(input);
// derives::derive_from_arg_matches(&input).into()
// }
/// Generates the `IntoApp` impl.
#[proc_macro_derive(IntoApp, attributes(clap))]
#[proc_macro_error]
pub fn into_app(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_into_app(&input).into()
}
/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap))]
#[proc_macro_error]
pub fn subcommand(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_subcommand(&input).into()
}

View file

@ -0,0 +1,8 @@
use clap::ArgEnum;
#[derive(ArgEnum, Debug)]
struct Opt {}
fn main() {
println!("{:?}", Opt::VARIANTS);
}

View file

@ -0,0 +1,7 @@
error: `#[derive(ArgEnum)]` only supports enums
--> $DIR/arg_enum_on_struct.rs:3:10
|
3 | #[derive(ArgEnum, Debug)]
| ^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -0,0 +1,6 @@
use clap::Subcommand;
#[derive(Subcommand, Debug)]
struct Opt {}
fn main() {}

View file

@ -0,0 +1,7 @@
error: `#[derive(Subcommand)]` only supports enums
--> $DIR/subcommand_on_struct.rs:3:10
|
3 | #[derive(Subcommand, Debug)]
| ^^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -41,9 +41,9 @@ pub use lazy_static;
#[macro_use]
#[allow(missing_docs)]
pub mod macros;
mod macros;
pub mod derive;
mod derive;
mod build;
mod mkeymap;