Skip to content

Commit

Permalink
Add impl<'_enum> From<&'_enum MyEnum> for MyEnumDiscriminants.
Browse files Browse the repository at this point in the history
  • Loading branch information
azriel91 committed Sep 24, 2018
1 parent f090491 commit 52e39e4
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 9 deletions.
58 changes: 49 additions & 9 deletions strum_macros/src/enum_discriminants.rs
Expand Up @@ -8,7 +8,6 @@ use helpers::{
pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let vis = &ast.vis;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let variants = match ast.data {
syn::Data::Enum(ref v) => &v.variants,
Expand Down Expand Up @@ -64,7 +63,25 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
discriminants.push(quote!{ #(#attrs)* #ident });
}

// Add match arms for `From< TheEnum >
// Ideally:
//
// * For `Copy` types, we `impl From<TheEnum> for TheEnumDiscriminants`
// * For `!Copy` types, we `impl<'enum> From<&'enum TheEnum> for TheEnumDiscriminants`
//
// That way we ensure users are not able to pass a `Copy` type by reference. However, the
// `#[derive(..)]` attributes are not in the parsed tokens, so we are not able to check if a
// type is `Copy`, so we just implement both.
//
// See <https://github.com/dtolnay/syn/issues/433>
// ---
// let is_copy = unique_meta_list(type_meta.iter(), "derive")
// .map(extract_list_metas)
// .map(|metas| {
// metas
// .filter_map(get_meta_ident)
// .any(|derive| derive.to_string() == "Copy")
// }).unwrap_or(false);

let arms = variants
.iter()
.map(|variant| {
Expand All @@ -83,6 +100,34 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {

quote! { #name::#ident #params => #discriminants_name::#ident }
}).collect::<Vec<_>>();
let from_fn_body = quote! { match val { #(#arms),* } };

let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let impl_from = quote! {
impl #impl_generics ::std::convert::From< #name #ty_generics > for #discriminants_name #where_clause {
fn from(val: #name #ty_generics) -> #discriminants_name {
#from_fn_body
}
}
};
let impl_from_ref = {
let mut generics = ast.generics.clone();

let lifetime = parse_quote!('_enum);
let enum_life = quote! { & #lifetime };
generics.params.push(lifetime);

// Shadows the earlier `impl_generics`
let (impl_generics, _, _) = generics.split_for_impl();

quote! {
impl #impl_generics ::std::convert::From< #enum_life #name #ty_generics > for #discriminants_name #where_clause {
fn from(val: #enum_life #name #ty_generics) -> #discriminants_name {
#from_fn_body
}
}
}
};

quote!{
/// Auto-generated discriminant enum variants
Expand All @@ -92,12 +137,7 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
#(#discriminants),*
}

impl #impl_generics ::std::convert::From< #name #ty_generics > for #discriminants_name #where_clause {
fn from(s: #name #ty_generics) -> #discriminants_name {
match s {
#(#arms),*
}
}
}
#impl_from
#impl_from_ref
}
}
1 change: 1 addition & 0 deletions strum_macros/src/lib.rs
Expand Up @@ -10,6 +10,7 @@
#![recursion_limit = "128"]

extern crate heck;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
Expand Down
6 changes: 6 additions & 0 deletions strum_tests/tests/enum_discriminants.rs
Expand Up @@ -157,3 +157,9 @@ fn from_test() {
assert_eq!(EnumIntoDiscriminants::A, EnumInto::A(true).into());
assert_eq!(EnumIntoDiscriminants::B, EnumInto::B(1).into());
}

#[test]
fn from_ref_test() {
assert_eq!(EnumIntoDiscriminants::A, (&EnumInto::A(true)).into());
assert_eq!(EnumIntoDiscriminants::B, (&EnumInto::B(1)).into());
}

0 comments on commit 52e39e4

Please sign in to comment.