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"),
}
}