slang_struct 0.0.1

Macro that converts Slang style struct definitions into rust structs
Documentation
use proc_macro2::{Ident, Span};
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, token::Brace, Path, Token, Type, TypePath};

struct SlangStructArray {
    slang_structs: Vec<SlangStruct>
}

struct SlangStruct {
    _struct_token: Token![struct],
    name: Ident,
    _brace_token: Brace,
    fields: Punctuated<Field, Token![;]>
}

struct Field {
    ty: Type,
    name: Ident
}

impl Parse for SlangStructArray {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut slang_structs = Vec::<SlangStruct>::new();
        while input.peek(Token![struct])  {
            slang_structs.push(input.parse()?);
        }

        Ok(SlangStructArray { slang_structs })
    }
}

impl Parse for SlangStruct {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        Ok(SlangStruct {
            _struct_token: input.parse()?,
            name: input.parse()?,
            _brace_token: braced!(content in input),
            fields: content.parse_terminated(Field::parse, Token![;])?
        })
    }
}

impl Parse for Field {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Field {
            ty: input.parse()?,
            name: input.parse()?
        })
    }
}

#[proc_macro]
pub fn slang_struct(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as SlangStructArray);
    let SlangStructArray {
        slang_structs
    } = input;

    let u64_type = Type::Path(TypePath {
        qself: None,
        path: Path::from(Ident::new("u64", Span::call_site())),
    });

    let mut ret = proc_macro2::TokenStream::new();
    for slang in slang_structs {
        let SlangStruct {
            _struct_token,
            name,
            _brace_token,
            fields
        } = slang;

        let names: Vec<Ident> = fields.iter().clone().map(|field| field.name.clone()).collect();
        let types: Vec<Type> = fields.iter().clone()
            .map(|field| match field.ty.to_token_stream().to_string().starts_with("Ptr") {
                true => u64_type.clone(),
                false => field.ty.clone()
            }).collect();

        ret.extend(quote! {
            #[repr(C)]
            #[derive(Clone, Copy, Default, bytemuck::Pod, bytemuck::Zeroable)]
            pub struct #name {
                #(#names: #types),*
            }
        });
    }

    ret.into()
}