from_variant/
lib.rs

1extern crate proc_macro;
2
3use swc_macros_common::prelude::*;
4use syn::*;
5
6/// Derives [`From`] for all variants. This only supports an enum where every
7/// variant has a single field.
8#[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}