cxc_derive 0.2.0

Derive crate for cxc
Documentation
use std::intrinsics::mir::Field;

use proc_macro::TokenStream as TokenStream1;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    parse_macro_input, Data, DataStruct, DeriveInput, Fields, Type, TypeArray, TypePath, TypePtr,
    TypeTuple, TypeBareFn, ReturnType, TypeParen, TypeReference, PathArguments, AngleBracketedGenericArguments, GenericArgument, FieldsNamed, punctuated::Punctuated,
};

#[proc_macro_derive(XcReflect)]
pub fn xc_reflect(tokens: TokenStream1) -> TokenStream1 {
    let input = parse_macro_input!(tokens as DeriveInput);
    let name = input.ident;

    let fields_punctuated = match input.data {
        Data::Struct(DataStruct {
            fields: Fields::Named(fields),
            ..
        }) => fields.named,
        Data::Enum(_) => todo!("XcReflect derive does not yet support enums"),
        _ => panic!("XcReflect derive does not yet support this type"),
    };

    TokenStream::from(output).into()
}

fn struct_token_stream(fields: FieldsNamed) -> TokenStream {
    let field_names = fields.named.iter().map(|field| {
        let field_name = field.ident.as_ref().unwrap();
        quote! { #field_name }
    });
    let field_types = fields.named
        .iter()
        .map(|field| type_data_to_tokens(&field.ty));

    quote! {
        impl cxc::XcReflect for #name {
            fn alias_code() -> String {
                let mut alias = stringify!(#name = ).to_string();
                alias += " { ";

                #(
                    alias += stringify!(#field_names :);
                    alias += " ";
                    alias += #field_types;
                    alias += ", ";
                )*

                alias += "}";

                alias
            }
        }
    }
}

fn type_data_to_tokens(typ: &Type) -> TokenStream {
    match typ {
        Type::Path(TypePath { path, .. }) => {
            let last_segment = path.segments.iter().last().cloned().unwrap();

            let type_name = last_segment.ident;
            let generic_arguments = last_segment.arguments;

            match generic_arguments {
                PathArguments::None => quote! { stringify! ( #type_name ) },
                PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) => {
                    let args = args.iter().filter_map(|arg| match arg {
                       GenericArgument::Type(typ) => Some(type_data_to_tokens(typ)),
                       GenericArgument::Const(_) => 
                           todo!("cxc_derive doesn't work with const generics"),
                       _ => None,
                    }).collect::<Vec<_>>();

                    quote! { &*(
                        String::from(stringify! ( #type_name )) + 
                            " < " + #( #args + ", ")+* + ">"
                    ) }
                }
                PathArguments::Parenthesized(_) => unreachable!(),
            }
        }
        Type::Array(TypeArray { elem, len, .. }) => {
            let base = type_data_to_tokens(&**elem);
            quote! {
                &*("[".to_string() + &*((#len).to_string()) + "]" + #base)
            }
        }
        Type::Tuple(TypeTuple { elems, .. }) => {
            let elems: Vec<_> = elems.iter().map(type_data_to_tokens).collect();
            quote! {
                &*("{ ".to_string() + #( #elems + ", ")+* + "}")
            }
        }
        Type::Ptr(TypePtr { elem, .. }) | Type::Reference(TypeReference { elem, .. }) => {
            let base = type_data_to_tokens(&**elem);
            quote! {
                &*("&".to_string() + #base)
            }
        }
        Type::BareFn(TypeBareFn { inputs, output, .. }) => {
            let args = inputs.iter().map(|i| type_data_to_tokens(&i.ty)).collect::<Vec<_>>();

            let ret = match output {
                ReturnType::Default => todo!("cannot derive void fn"),
                ReturnType::Type(_, ret) => type_data_to_tokens(ret),
            };

            quote! {
                "(" + #(#args + ", ")+* + "); " + #ret
            }
        }
        Type::Paren(TypeParen { elem, .. }) => type_data_to_tokens(&*elem),
        _ => todo!("XcReflect derive does not support this field type"),
    }
}