From ff4391ad15ecb1ebf4c041b34b53505dc1faf494 Mon Sep 17 00:00:00 2001 From: realbigsean Date: Fri, 2 Feb 2024 11:03:57 -0500 Subject: [PATCH] make specific variant work --- src/lib.rs | 17 ++++++-- tests/meta_variant.rs | 94 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d8c491e..e228bc4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,7 +277,10 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream { panic!("can't configure `only` and `getter` on the same field"); } else if field_opts.meta_only.is_some() && field_opts.getter.is_some() { panic!("can't configure `meta_only` and `getter` on the same field"); - } else if field_opts.only.is_none() && field_opts.partial_getter.is_some() { + } else if field_opts.only.is_none() + && field_opts.meta_only.is_none() + && field_opts.partial_getter.is_some() + { panic!("can't set `partial_getter` options on common field"); } else if field_opts.flatten.is_some() && field_opts.only.is_some() { panic!("can't set `flatten` and `only` on the same field"); @@ -407,19 +410,27 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream { for (variant_key, struct_name) in variant_combinations.zip(struct_names.iter()) { let fields = &variant_fields[&variant_key]; - // TODO: think about how to handle this with meta let specific_struct_attributes = opts .specific_variant_attributes .as_ref() .and_then(|sv| sv.get(&variant_key.variant)) .map_or(&[][..], |attrs| &attrs.metas); + let specific_struct_attributes_meta = opts + .specific_variant_attributes + .as_ref() + .and_then(|sv| variant_key.meta_variant.and_then(|mv| sv.get(&mv))) + .map_or(&[][..], |attrs| &attrs.metas); + let spatt = specific_struct_attributes + .iter() + .chain(specific_struct_attributes_meta.iter()) + .unique(); let variant_code = quote! { #( #[#universal_struct_attributes] )* #( - #[#specific_struct_attributes] + #[#spatt] )* #visibility struct #struct_name #decl_generics #where_clause { #( diff --git a/tests/meta_variant.rs b/tests/meta_variant.rs index 53071d13..a8db818b 100644 --- a/tests/meta_variant.rs +++ b/tests/meta_variant.rs @@ -134,3 +134,97 @@ fn meta_variant_flatten() { assert!(message_h.inner_b_read_upper().is_err()); assert_eq!(message_h.inner_b_write_upper().unwrap().w, 3); } + +#[test] +fn meta_variants_map_macro() { + #[superstruct( + meta_variants(Juicy, Sour), + variants(Apple, Orange), + variant_attributes(derive(Debug, PartialEq)) + )] + #[derive(Debug, PartialEq)] + pub struct Fruit { + #[superstruct(getter(copy))] + id: u64, + #[superstruct(only(Apple), partial_getter(copy))] + description: &'static str, + #[superstruct(meta_only(Juicy))] + name: &'static str, + } + + fn increment_id(id: Fruit) -> Fruit { + map_fruit!(id, |mut inner, cons| { + *inner.id_mut() += 1; + cons(inner) + }) + } + + fn get_id_via_ref<'a>(fruit_ref: FruitRef<'a>) -> u64 { + map_fruit_ref!(&'a _, fruit_ref, |inner, _| { inner.id() }) + } + + assert_eq!( + increment_id(Fruit::Juicy(FruitJuicy::Orange(FruitJuicyOrange { + id: 10, + name: "orange" + }))) + .id(), + get_id_via_ref( + Fruit::Juicy(FruitJuicy::Orange(FruitJuicyOrange { + id: 11, + name: "orange" + })) + .to_ref() + ) + ); +} + +#[test] +fn meta_variants_exist_specific_attributes() { + #[superstruct( + meta_variants(One, Two), + 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, + } + + fn copy(t: T) -> (T, T) { + (t, t) + } + + let x = ThingOneIsCopy { x: 0 }; + assert_eq!(copy(x), (x, x)); + let x = ThingTwoIsCopy { x: 0 }; + assert_eq!(copy(x), (x, x)); +} + +#[test] +fn meta_variants_have_specific_attributes() { + #[superstruct( + meta_variants(IsCopy, IsNotCopy), + variants(One, Two), + variant_attributes(derive(Debug, PartialEq, Clone)), + specific_variant_attributes(IsCopy(derive(Copy))) + )] + #[derive(Clone, PartialEq, Debug)] + pub struct Ting { + pub x: u64, + #[superstruct(meta_only(IsNotCopy))] + pub y: String, + } + + fn copy(t: T) -> (T, T) { + (t, t) + } + + let x = TingIsCopyOne { x: 0 }; + assert_eq!(copy(x), (x, x)); + let x = TingIsCopyTwo { x: 0 }; + assert_eq!(copy(x), (x, x)); +}