aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/func/register_metadata.rs
blob: a1f996529ecb0d4ff8fb1904994165f48386bbd0 (plain) (blame)
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
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::TypePath;
use syn::spanned::Spanned;

use crate::METADATA;
use crate::get_global_set;

/// Parses and registers a metadata mapping of the form
/// `register_metadata!(EntryGreet, Description)`.
///
/// Stores a match-arm-style string entry `Self::EntryGreet => { ... }` that is
/// later consumed by `program_final_gen!` to generate the `get_metadata`
/// method of `ProgramCollect`.
pub(crate) fn register_metadata_impl(input: TokenStream) -> TokenStream {
    // Parse the input as a comma-separated list of type arguments.
    let input_parsed = syn::parse_macro_input!(
        input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated
    );

    if input_parsed.len() != 2 {
        return syn::Error::new(
            input_parsed.span(),
            "Expected exactly two comma-separated arguments: `EntryVariant, MetadataType`",
        )
        .to_compile_error()
        .into();
    }

    let entry_expr = &input_parsed[0];
    let metadata_expr = &input_parsed[1];

    let entry_type = match syn::parse2::<TypePath>(entry_expr.to_token_stream()) {
        Ok(ty) => ty,
        Err(e) => return e.to_compile_error().into(),
    };
    let metadata_type = match syn::parse2::<TypePath>(metadata_expr.to_token_stream()) {
        Ok(ty) => ty,
        Err(e) => return e.to_compile_error().into(),
    };

    let entry_str = build_metadata_entry(&entry_type, &metadata_type).to_string();

    get_global_set(&METADATA).lock().unwrap().insert(entry_str);

    quote::quote! {}.into()
}

/// Builds the match-arm entry for `get_metadata`, matching on the enum variant
/// and then on the requested `TypeId`.
fn build_metadata_entry(
    entry_type: &TypePath,
    metadata_type: &TypePath,
) -> proc_macro2::TokenStream {
    let enum_variant = entry_type.path.segments.last().unwrap().ident.clone();
    quote::quote! {
        Self::#enum_variant => {
            let __metadata_type_id = ::std::any::TypeId::of::<#metadata_type>();
            match type_id {
                _ if type_id == __metadata_type_id => Some(::std::boxed::Box::new(
                    <#entry_type as ::mingling::Metadata<#metadata_type>>::init_metadata(),
                ) as ::std::boxed::Box<dyn ::std::any::Any>),
                _ => None,
            }
        }
    }
}