Add derive_for_all global attribute (defaulting to Debug)

In most cases, I expect that users just want all structs to derive
`Debug` or maybe also `Clone`.
This commit is contained in:
Lukas Kalbertodt
2021-04-29 23:45:40 +02:00
parent 651a06b252
commit 5075b4df17
4 changed files with 53 additions and 30 deletions

View File

@@ -3,7 +3,8 @@ mod config {
use std::path::PathBuf;
confique::config! {
#[derive(Debug, Clone)]
#![derive_for_all(Debug, Clone)]
log: {
/// Determines how many messages are logged. Log messages below
/// this level are not emitted. Possible values: "trace", "debug",

View File

@@ -7,6 +7,7 @@ use proc_macro2::{Ident, TokenStream};
pub(crate) struct Input {
pub(crate) root: Node,
pub(crate) visibility: Option<TokenStream>,
pub(crate) derive_for_all: Option<TokenStream>,
}
/// One node in the tree of the configuration format. Can either be a leaf node

View File

@@ -217,19 +217,12 @@ fn gen_types(input: &Input, visibility: &TokenStream) -> TokenStream {
// Main type definition
let doc = &node.doc;
let attrs = &node.attrs;
// We add some derives if the user has not specified any
let derive = if attrs.iter().any(|attr| attr.path.is_ident("derive")) {
quote! {}
} else {
quote! { #[derive(Debug)] }
};
let derives = input.derive_for_all.clone().unwrap_or(quote! { Debug });
main_types.extend(quote! {
#( #[doc = #doc] )*
#( #attrs )*
#derive
#[derive( #derives )]
#visibility struct #typename {
#main_fields
}

View File

@@ -1,4 +1,5 @@
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{
Error, Ident,
parse::{Parse, ParseStream},
@@ -13,8 +14,10 @@ use crate::ast::{Expr, Input, Leaf, Node, NodeKind, Obj};
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let mut outer_attrs = input.call(syn::Attribute::parse_inner)?;
let doc = extract_doc(&mut outer_attrs)?;
let visibility = extract_visibility(&mut outer_attrs)?;
let derive_for_all = extract_single_list_attr("derive_for_all", &mut outer_attrs)?;
let doc = extract_doc(&mut outer_attrs)?;
let typename = extract_typename(&mut outer_attrs)?;
// Extract attributes that we will just forward/emit verbatim. Well, not
@@ -24,8 +27,9 @@ impl Parse for Input {
a.style = syn::AttrStyle::Outer;
}
assert_no_extra_attrs(&outer_attrs)?;
// Parse children
let children = input.call(<Punctuated<_, syn::Token![,]>>::parse_terminated)?;
let root = Node {
@@ -38,7 +42,7 @@ impl Parse for Input {
}),
};
Ok(Self { root, visibility })
Ok(Self { root, visibility, derive_for_all })
}
}
@@ -177,33 +181,57 @@ fn extract_attrs(names: &[&str], attrs: &mut Vec<syn::Attribute>) -> Vec<syn::At
matches
}
fn extract_single_name_value_attr(
fn extract_single_attr(
name: &str,
attrs: &mut Vec<syn::Attribute>,
) -> Result<Option<syn::Lit>, Error> {
let mut filtered = attrs.iter().filter(|attr| attr.path.is_ident(name));
let meta = match filtered.next() {
) -> Result<Option<syn::Attribute>, Error> {
let attr = match attrs.iter().position(|attr| attr.path.is_ident(name)) {
None => return Ok(None),
Some(attr) => attr.parse_meta()?,
Some(pos) => attrs.remove(pos),
};
let nv = match meta {
syn::Meta::NameValue(nv) => nv,
other => {
let msg = format!(r#"expected `name = "value"` attribute syntax for `{}`"#, name);
return Err(Error::new(other.span(), msg));
}
};
if let Some(dupe) = filtered.next() {
if let Some(dupe) = attrs.iter().find(|attr| attr.path.is_ident(name)) {
let msg = format!("duplicate `{}` attribute", name);
return Err(Error::new(dupe.span(), msg));
}
// Remove the attribute from the vector
attrs.retain(|attr| !attr.path.is_ident(name));
Ok(Some(attr))
}
Ok(Some(nv.lit))
fn extract_single_name_value_attr(
name: &str,
attrs: &mut Vec<syn::Attribute>,
) -> Result<Option<syn::Lit>, Error> {
let attr = match extract_single_attr(name, attrs)? {
None => return Ok(None),
Some(attr) => attr,
};
match attr.parse_meta()? {
syn::Meta::NameValue(nv) => Ok(Some(nv.lit)),
other => {
let msg = format!(r#"expected `name = "value"` attribute syntax for `{}`"#, name);
Err(Error::new(other.span(), msg))
}
}
}
fn extract_single_list_attr(
name: &str,
attrs: &mut Vec<syn::Attribute>,
) -> Result<Option<TokenStream>, Error> {
let attr = match extract_single_attr(name, attrs)? {
None => return Ok(None),
Some(attr) => attr,
};
match attr.parse_meta()? {
syn::Meta::List(list) => Ok(Some(list.nested.to_token_stream())),
other => {
let msg = format!(r#"expected `{}(...)` attribute syntax"#, name);
return Err(Error::new(other.span(), msg));
}
}
}
fn assert_string_lit(lit: syn::Lit) -> Result<String, Error> {