summaryrefslogtreecommitdiff
path: root/engine-macros/src/reflection.rs
blob: 2180084715a67ccabdbf621449c4110e9008472f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use crate::reflection::options_attr::OptionsAttr;

mod default_value;
mod enum_impl;
mod field;
mod options_attr;
mod struct_impl;
mod visibility;

pub fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
{
    let input = syn::parse2::<syn::Item>(input).unwrap();

    let options = get_options(&input);

    match input {
        syn::Item::Struct(input) => struct_impl::generate(input, options),
        syn::Item::Enum(input) => enum_impl::generate(input, options),
        _ => panic!("Invalid input"),
    }
}

pub fn get_options(input: &syn::Item) -> OptionsAttr
{
    let (attrs, generic_params) = match &input {
        syn::Item::Struct(input) => (input.attrs.as_slice(), &input.generics.params),
        syn::Item::Enum(input) => (input.attrs.as_slice(), &input.generics.params),
        _ => (
            const { &[] }.as_slice(),
            &syn::punctuated::Punctuated::<syn::GenericParam, syn::Token![,]>::new(),
        ),
    };

    if attrs
        .iter()
        .filter(|attr| attr.path().is_ident("reflection"))
        .count()
        > 1
    {
        panic!("Multiple 'reflection' attributes are not allowed");
    }

    let options = match attrs
        .iter()
        .find_map(|attr| {
            if attr.path().is_ident("reflection") {
                return Some(attr.parse_args::<OptionsAttr>());
            }

            None
        })
        .transpose()
    {
        Ok(attr) => attr.unwrap_or_default(),
        Err(err) => {
            panic!("{err}");
        }
    };

    if options.impl_with_generics.is_empty() && !generic_params.is_empty() {
        panic!(concat!(
            "Generic types deriving Reflection must specify which ",
            "generics to use in the generated impl(s) using the 'reflection' attribute\n",
            "For example: #[reflection(impl_with_generics(<u32, String>, <u8, u16>))]"
        ));
    }

    if !options.impl_with_generics.is_empty() && generic_params.is_empty() {
        panic!("Specifying 'impl_with_generics' for non-generic types is not allowed");
    }

    options
}