| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| extern crate proc_macro; |
| |
| use { |
| darling::{ast, FromDeriveInput, FromField, FromVariant}, |
| proc_macro2::TokenStream, |
| quote::quote, |
| std::collections::HashMap, |
| syn::{parse_macro_input, Ident, Type}, |
| }; |
| |
| #[derive(FromField)] |
| struct EnumField { |
| ty: Type, |
| } |
| |
| #[derive(FromVariant)] |
| struct EnumVariant { |
| ident: Ident, |
| fields: ast::Fields<EnumField>, |
| } |
| |
| #[derive(FromDeriveInput)] |
| #[darling(supports(enum_newtype))] |
| struct FromEnumOpts { |
| ident: Ident, |
| data: ast::Data<EnumVariant, ()>, |
| } |
| |
| fn from_enum_derive_impl(input: syn::DeriveInput) -> TokenStream { |
| let opts = match FromEnumOpts::from_derive_input(&input) { |
| Ok(opts) => opts, |
| Err(e) => return e.write_errors(), |
| }; |
| let variants = match opts.data { |
| ast::Data::Enum(variants) => variants, |
| _ => unreachable!("guaranteed to be an enum"), |
| }; |
| let enum_ident = &opts.ident; |
| let mut impls = HashMap::new(); |
| for variant in variants { |
| let variant_ident = &variant.ident; |
| let field = &variant.fields.fields[0]; |
| let field_ty = &field.ty; |
| let insertion = impls.insert( |
| field_ty.clone(), |
| quote! { |
| impl from_enum::FromEnum < #enum_ident > for #field_ty { |
| fn from_enum(e: & #enum_ident) -> Option<&Self> { |
| match e { |
| #enum_ident :: #variant_ident (inner) => Some(inner), |
| _ => None, |
| } |
| } |
| } |
| |
| impl std::convert::From < #field_ty > for #enum_ident { |
| fn from(f: #field_ty) -> Self { |
| Self :: #variant_ident (f) |
| } |
| } |
| }, |
| ); |
| if insertion.is_some() { |
| return darling::Error::custom("no two variants can contain the same type") |
| .with_span(&variant_ident) |
| .write_errors(); |
| } |
| } |
| let impls: Vec<&TokenStream> = impls.values().collect(); |
| |
| quote! { |
| #(#impls)* |
| } |
| } |
| |
| #[proc_macro_derive(FromEnum)] |
| pub fn from_enum_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| from_enum_derive_impl(parse_macro_input!(input)).into() |
| } |