1extern crate proc_macro;
2
3use swc_macros_common::prelude::*;
4use syn::*;
5
6#[proc_macro_derive(FromVariant, attributes(from_variant))]
9pub fn derive_from_variant(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
11
12 let item = derive(input)
13 .into_iter()
14 .fold(TokenStream::new(), |mut t, item| {
15 item.to_tokens(&mut t);
16 t
17 });
18
19 print("derive(FromVariant)", item)
20}
21
22fn is_ignored(attrs: &[syn::Attribute]) -> bool {
23 attrs
24 .iter()
25 .filter(|attr| attr.path().is_ident("from_variant"))
26 .any(|attr| {
27 let mut is_unknown = false;
28 attr.parse_nested_meta(|meta| {
29 is_unknown |= meta.path.is_ident("ignore");
30 Ok(())
31 })
32 .unwrap();
33 is_unknown
34 })
35}
36
37fn derive(
38 DeriveInput {
39 generics,
40 data,
41 ident,
42 ..
43 }: DeriveInput,
44) -> Vec<ItemImpl> {
45 let variants = match data {
46 Data::Enum(DataEnum { variants, .. }) => variants,
47 _ => panic!("#[derive(FromVariant)] only works for an enum."),
48 };
49
50 let mut from_impls: Vec<ItemImpl> = Vec::new();
51
52 for v in variants {
53 if is_ignored(&v.attrs) {
54 continue;
55 }
56
57 let variant_name = v.ident;
58 match v.fields {
59 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
60 if unnamed.len() != 1 {
61 panic!(
62 "#[derive(FromVariant)] requires all variants to be tuple with exactly \
63 one field"
64 )
65 }
66 let field = unnamed.into_iter().next().unwrap();
67
68 let variant_type = &field.ty;
69
70 let from_impl: ItemImpl = parse_quote!(
71 impl From<#variant_type> for #ident {
72 fn from(v: #variant_type) -> Self {
73 #ident::#variant_name(v)
74 }
75 }
76 );
77
78 let from_impl = from_impl.with_generics(generics.clone());
79
80 from_impls.push(from_impl);
81 }
82 _ => panic!(
83 "#[derive(FromVariant)] requires all variants to be tuple with exactly one field"
84 ),
85 }
86 }
87
88 from_impls
89}