Skip to content

Commit

Permalink
Implement variant-specific attributes (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelsproul authored Dec 6, 2022
1 parent baa118c commit efac781
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
12 changes: 12 additions & 0 deletions book/src/config/struct.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ This can be used to derive traits, perform conditional compilation, etc.

**Format**: any.

## Specific variant attributes

```
#[superstruct(specific_variant_attributes(A(...), B(...), ...))]
```

Similar to `variant_attributes`, but applies the attributes _only_ to the named variants. This
is useful if e.g. one variant needs to derive a trait which the others cannot, or if another
procedural macro is being invoked on the variant struct which requires different parameters.

**Format**: zero or more variant names, with variant attributes nested in parens

## `Ref` attributes

```
Expand Down
17 changes: 15 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct StructOpts {
/// List of attributes to apply to the variant structs.
#[darling(default)]
variant_attributes: Option<NestedMetaList>,
/// List of attributes to apply to a selection of named variant structs.
#[darling(default)]
specific_variant_attributes: Option<HashMap<Ident, NestedMetaList>>,
/// List of attributes to apply to the generated Ref type.
#[darling(default)]
ref_attributes: Option<NestedMetaList>,
Expand Down Expand Up @@ -207,16 +210,26 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
}

// Generate structs for all of the variants.
let struct_attributes = opts
let universal_struct_attributes = opts
.variant_attributes
.as_ref()
.map_or(&[][..], |attrs| &attrs.metas);

for (variant_name, struct_name) in variant_names.iter().zip(struct_names.iter()) {
let fields = &variant_fields[variant_name];

let specific_struct_attributes = opts
.specific_variant_attributes
.as_ref()
.and_then(|sv| sv.get(&variant_name))
.map_or(&[][..], |attrs| &attrs.metas);

let variant_code = quote! {
#(
#[#struct_attributes]
#[#universal_struct_attributes]
)*
#(
#[#specific_struct_attributes]
)*
#visibility struct #struct_name #decl_generics #where_clause {
#(
Expand Down
23 changes: 23 additions & 0 deletions tests/specific_variant_attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use superstruct::superstruct;

#[superstruct(
variants(IsCopy, IsNotCopy),
variant_attributes(derive(Debug, PartialEq, Clone)),
specific_variant_attributes(IsCopy(derive(Copy)))
)]
#[derive(Clone, PartialEq, Debug)]
pub struct Thing {
pub x: u64,
#[superstruct(only(IsNotCopy))]
pub y: String,
}

#[test]
fn copy_the_thing() {
fn copy<T: Copy>(t: T) -> (T, T) {
(t, t)
}

let x = ThingIsCopy { x: 0 };
assert_eq!(copy(x), (x, x));
}

0 comments on commit efac781

Please sign in to comment.